1. 程式人生 > >java記憶體結構和垃圾回收機制

java記憶體結構和垃圾回收機制

java和c語言不同

開發c語言專案,需要程式設計師手動清理垃圾,而java有垃圾自動回收機制。它可以減少記憶體異常發生的機率。

一、java的記憶體結構,可以分為五個區域:

1、程式計數器,它儲存的是程式當前執行的指令的地址,也就是說是用來指示 執行哪條指令的。在多執行緒的環境下,每個執行緒都要有自己的程式計數器,這樣線上程切換的時候,每個執行緒能恢復到執行緒之前的執行位置。

2、java棧,Java棧中存放的是一個個的棧幀,每個棧幀對應一個被呼叫的方法,在棧幀中包括區域性變量表、運算元棧(Operand Stack)、常量池、方法返回地址和一些額外的附加資訊。當執行緒執行一個方法時,就會建立一個對應的棧幀,執行開始叫入棧,執行結束叫出棧。對於所有的程式設計語言來說,棧這部分空間對程式設計師來說是不透明的,這部分空間的分配和釋放都是由系統自動實施的。由於每個執行緒正在執行的方法可能不同,因此每個執行緒都會有一個自己的Java棧,互不干擾。

3、本地方法棧(native),它和java棧很相似,只不過,java棧是為java方法服務的,而本地方法棧是為執行本地方法(Native Method)服務的。

4.、堆,用來儲存物件本身以及陣列。在C語言中,程式設計師通過malloc函式和free函式在堆上申請和釋放空間。在java中,程式設計師基本不用去關心空間釋放的問題,Java的垃圾回收機制會自動進行處理。因此這部分空間也是Java垃圾收集器管理的主要區域。堆是被所有執行緒共享的,在JVM中只有一個堆。

5、方法區,用於儲存已經被虛擬機器載入的類資訊(即載入類時需要載入的資訊,包括版本、field、方法、介面等資訊)、final常量、靜態變數、編譯器即時編譯的程式碼等。方法區和堆一樣是執行緒共享的區域。

 

二、java垃圾回收演算法

1、標記-清理,首先標記出所有需要回收的物件,在標記完成之後統一回收掉所有被標記的物件。簡而言之,找沒有用的物件,然後清理。標記-清除演算法的缺點有兩個:首先,效率問題,標記和清除效率都不高。其次,標記清除之後會產生大量的不連續的記憶體碎片,空間碎片太多會導致當程式需要為較大物件分配記憶體時無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作。所以這個方法會導致效率和空間問題。

2、標記-整理,將可用記憶體按容量分成大小相等的兩塊,每次只使用其中一塊,當這塊記憶體使用完了,就將還存活的物件複製到另一塊記憶體上去,然後把使用過的記憶體空間一次清理掉。這樣使得每次都是對其中一塊記憶體進行回收,記憶體分配時不用考慮記憶體碎片等複雜情況,只需要移動堆頂指標,按順序分配記憶體即可,實現簡單,執行高效。複製演算法的缺點顯而易見,可使用的記憶體降為原來一半。

3、分代回收,將記憶體分為新生代和年老代,新生代分為Eden區、Survivor from和Survivor to三部分。當新建立物件時,一般在新生代中分配記憶體空間,當新生代垃圾收集器回收幾次之後仍然存活的物件會被移動到年老代記憶體中,當大物件在新生代中無法找到足夠的連續記憶體時也直接在年老代中建立。生代使用複製和標記-清除垃圾收集演算法。在新生代裡,98%的物件是朝生夕死的短生命週期物件,所以不需要將新生代劃分為容量大小相等的兩部分記憶體,而是將新生代分為Eden區,Survivor from和Survivor to三部分,其佔新生代記憶體容量預設比例分別為8:1:1,其中Survivor from和Survivor to總有一個區域是空白,只有Eden和其中一個Survivor總共90%的新生代容量用於為新建立的物件分配記憶體,只有10%的Survivor記憶體浪費,當新生代記憶體空間不足需要進行垃圾回收時,仍然存活的物件被複制到空白的Survivor記憶體區域中,Eden和非空白的Survivor進行標記-清理回收,兩個Survivor區域是輪換的。當新生代中無足夠空間為物件建立分配記憶體,年老代中記憶體回收也無法回收到足夠的記憶體空間,並且新生代和年老代空間無法在擴充套件時,堆就會產生OutOfMemoryError異常。

附帶idea配置vm  option引數示例詳解:

-Xms768m:設定JVM初始堆記憶體為768m。此值可以設定與-Xmx相同,以避免每次垃圾回收完成後JVM重新分配記憶體。 
-Xmx768m:設定JVM最大堆記憶體為768m。 
-Xss128k:設定每個執行緒的棧大小。JDK5.0以後每個執行緒棧大小為1M,之前每個執行緒棧大小為256K。應當根據應用的執行緒所需記憶體大小進行調整。在相同實體記憶體下,減小這個值能生成更多的執行緒。但是作業系統對一個程序內的執行緒數還是有限制的,不能無限生成,經驗值在3000~5000左右。需要注意的是:當這個值被設定的較大(例如>2MB)時將會在很大程度上降低系統的效能。 
-Xmn2g:設定年輕代大小為2G。在整個堆記憶體大小確定的情況下,增大年輕代將會減小年老代,反之亦然。此值關係到JVM垃圾回收,對系統性能影響較大,官方推薦配置為整個堆大小的3/8。 
-XX:NewSize=1024m:設定年輕代初始值為1024M。 
-XX:MaxNewSize=1024m:設定年輕代最大值為1024M。 
-XX:OldSize=2048m : 設定JVM啟動分配的老年代記憶體大小.
-XX:PermSize=256m:設定持久代初始值為256M。 永久代不屬於堆記憶體,堆記憶體只包含新生代和老年代
-XX:MaxPermSize=256m:設定持久代最大值為256M。 
-XX:NewRatio=4:設定年輕代(包括1個Eden和2個Survivor區)與年老代的比值。表示年輕代比年老代為1:4。 
-XX:SurvivorRatio=4:設定年輕代中Eden區與Survivor區的比值。表示2個Survivor區(JVM堆記憶體年輕代中預設有2個大小相等的Survivor區)與1個Eden區的比值為2:4,即1個Survivor區佔整個年輕代大小的1/6。 
-XX:MaxTenuringThreshold=7:表示一個物件如果在Survivor區(救助空間)移動了7次還沒有被垃圾回收就進入年老代。如果設定為0的話,則年輕代物件不經過Survivor區,直接進入年老代,對於需要大量常駐記憶體的應用,這樣做可以提高效率。如果將此值設定為一個較大值,則年輕代物件會在Survivor區進行多次複製,這樣可以增加物件在年輕代存活時間,增加物件在年輕代被垃圾回收的概率,減少Full GC的頻率,這樣做可以在某種程度上提高服務穩定性。
我的配置~

-Xms3072m
-Xmx3072m
-XX:NewSize=400m
-XX:PermSize=128m
-XX:MaxPermSize=256m
-XX:NewRatio=4
-XX:SurvivorRatio=4
-Dfile.encoding=UTF-8