JVM堆棧 java
棧是運行時的單位,而堆是存儲的單位。
棧解決程序的運行問題,即程序如何執行,或者說如何處理數據;堆解決的是數據存儲的問題,即數據怎麼放、放在哪兒。
在Java中一個線程就會相應有一個線程棧與之對應,由於不一樣的線程執行邏輯有所不一樣,所以須要一個獨立的線程棧。而堆則是全部線程共享的。棧由於是運行單位,所以裏面存儲的信息都是跟當前線程(或程序)相關信息的。包括局部變量、程序運行狀態、方法返回值等等。而堆只負責存儲對象信息。緩存
爲何要把堆和棧區分出來呢?棧中不是也能夠存儲數據嗎?
第一,從軟件設計的角度看,棧表明了處理邏輯,而堆表明了數據。這樣分開,使得處理邏輯更爲清晰,分而治之的思想。這種隔離、模塊化的思想在軟件設計的方方面面都有體現。數據結構
第二,堆與棧的分離,使得堆中的內容能夠被多個棧共享(也能夠理解爲多個線程訪問同一個對象)。這種共享的收益是不少的。一方面這種共享提供了一種有效的數據交互方式(如:共享內存),另外一方面,堆中的共享常量和緩存能夠被全部棧訪問,節省了空間。模塊化
第三,棧由於運行時的須要,好比保存系統運行的上下文,須要進行地址段的劃分。因爲棧只能向上增加,所以就會限制住棧存儲內容的能力。而堆不一樣,堆中的對象是能夠根據須要動態增加的,所以棧和堆的拆分,使得動態增加成爲可能,相應棧中只需記錄堆中的一個地址便可。函數
第四,面向對象就是堆和棧的完美結合。其實,面向對象方式的程序與之前結構化的程序在執行上沒有任何區別。可是,面向對象的引入,使得對待問題的思考方式發生了改變,而更接近於天然方式的思考。當咱們把對象拆開,你會發現,對象的屬性其實就是數據,存放在堆中;而對象的行爲(方法),就是運行邏輯,放在棧中。咱們在編寫對象的時候,其實即編寫了數據結構,也就是編寫的處理數據的邏輯。不得不認可,面向對象的設計,確實很美。spa
在Java中,Main函數就是棧的起始點,也是程序的起始點。操作系統
堆中存什麼?棧中存什麼?
堆中存的是對象。棧中存的是基本數據類型和堆中對象的引用。一個對象的大小是不可估計的,或者說是能夠動態變化的,可是在棧中,一個對象只對應了一個4btye的引用(堆棧分離的好處:)。線程
爲何不把基本類型放堆中呢?
由於其佔用的空間通常是1~8個字節——須要空間比較少,並且由於是基本類型,因此不會出現動態增加的狀況——長度固定,所以棧中存儲就夠了,若是把他存在堆中是沒有什麼意義的。能夠這麼說,基本類型和對象的引用都是存放在棧中,並且都是幾個字節的一個數,所以在程序運行時,他們的處理方式是統一的。可是基本類型、對象引用和對象自己就有所區別了,由於一個是棧中的數據一個是堆中的數據。最多見的一個問題就是,Java中參數傳遞時的問題。設計
Java對象的大小
在Java中,一個空Object對象(Object obj=new Object();)的大小是8byte,這個大小隻是保存堆中一個沒有任何屬性的對象的大小。在程序中完成了一個Java對象的生命,可是它所佔的空間爲:4byte+8byte。4byte是上面部分所說的Java棧中保存引用的所須要的空間。而那8byte則是Java堆中對象的信息。由於全部的Java非基本類型的對象都須要默認繼承Object對象,所以不論什麼樣的Java對象,其大小都必須是大於8byte。對象
引用類型:對象引用類型分爲強引用、軟引用、弱引用和虛引用。
強引用:就是通常聲明對象是時虛擬機生成的引用,強引用環境下,垃圾回收時須要嚴格判斷當前對象是否被強引用,若是被強引用,則不會被垃圾回收。 例如:User user=new User(); //user就是強引用。
軟引用:軟引用通常被作爲緩存來使用。與強引用的區別是,軟引用在垃圾回收時,虛擬機會根據當前系統的剩餘內存來決定是否對軟引用進行回收。若是剩餘內存比較緊張,則虛擬機會回收軟引用所引用的空間;若是剩餘內存相對富裕,則不會進行回收。
例如: SoftReference<Object> sf = new SoftReference<Object>(obj); //sf就是一個軟引用。
弱引用:弱引用與軟引用相似,都是做爲緩存來使用。但與軟引用不一樣,弱引用在進行垃圾回收時,是必定會被回收掉的,所以其生命週期只存在於一個垃圾回收週期內。
例如: WeakReference<Object> wf = new WeakReference<Object>(obj); //wf就是一個弱引用。
虛引用:與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收。
例如: PhantomReference<Object> pf = new PhantomReference<Object>(obj); //pf就是一個虛引用。
異常: java.lang.OutOfMemoryError: Java heap space
解決:通常就是根據垃圾回收先後狀況對比,同時根據對象引用狀況(常見的集合對象引用)分析,基本均可以找到泄漏點。
異常:java.lang.OutOfMemoryError: PermGen space
解決:主要緣由就是大量動態反射生成的類不斷被加載,最終致使Perm區被佔滿。
增長持久層空間大小: -XX:MaxPermSize=32m
換用JDK。好比JRocket。
異常:java.lang.StackOverflowError
解決: 通常就是遞歸沒返回,或者循環調用形成,檢查遞歸調用和循環調用。
異常:Fatal: Stack size too small
說明:java中一個線程的空間大小是有限制的。JDK5.0之後這個值是1M。與這個線程相關的數據將會保存在其中。可是當線程空間滿了之後,將會出現上面異常。
解決: 增長線程棧大小:-Xss2m。若這個配置沒法解決根本問題,還要看代碼部分是否有形成泄漏的部分。
異常:java.lang.OutOfMemoryError: unable to create new native thread 說明:這個異常是因爲操做系統沒有足夠的資源來產生這個線程形成的。系統建立線程時,除了要在Java堆中分配內存外,操做系統自己也須要分配資源來建立線程。所以,當線程數量大到必定程度之後,堆中或許還有空間,可是操做系統分配不出資源來了,就出現這個異常了。分配給Java虛擬機的內存愈多,系統剩餘的資源就越少,所以,當系統內存固定時,分配給Java虛擬機的內存越多,那麼,系統總共可以產生的線程也就越少,二者成反比的關係。同時,能夠經過修改-Xss來減小分配給單個線程的空間,也能夠增長系統總共內生產的線程數。 解決: 從新設計系統減小線程數量。線程數量不能減小的狀況下,經過 -Xss128k 減少單個線程大小。以便能生產更多的線程。