首頁 >Java >java教程 >淺談java記憶體分配與回收策略

淺談java記憶體分配與回收策略

巴扎黑
巴扎黑原創
2017-06-26 10:41:411148瀏覽

一、導論

  java技術體系中所提到的記憶體自動化管理歸根究底就是記憶體的分配與回收兩個問題,之前已經和大家談過java回收的相關知識,今天來和大家聊聊java物件的在記憶體中的分配。通俗的講,物件的記憶分配就是在堆上的分配,物件主要分配在新生代的Eden上(關於物件在記憶體上的分代在垃圾回收中會補上,想了解的也可以參考《深入理解java虛擬機器》),如果啟動了本地執行緒分配緩衝,講按執行緒優先在TLAB上分配。少數情況下也是直接在老年代中分配。

二、經典的分配策略

1、物件優先在Eden上分配

  一般情況下物件都是優先分配在Eden上,當Eden沒有足夠的空間進行分配時,jvm會發起一次Minor GC。如果還是沒有足夠的空間分配,後面還有另外的措施,下面會提到。

  設定虛擬機器的偶記日誌參數-XX:+PrintGCDetails,在垃圾回收的時候會列印記憶體的回收日誌,並且在進程退出的時候會輸出目前記憶體各區域的分配。下面來看下具體的例子,首先需要設定jvm的參數-Xms20m -Xmx20m -Xmn10m,這三個參數說明java堆大小為20M,且不可擴展,其中10M分配給新生代,剩下的10M分配給老年代。 -XX:SurvivorRatio=8是jvm預設的新生代中Eden和Survivor比例,預設為8:1。原因是新生代中的物件98%都會在下次GC的時候回收掉,所以很適合採用複製演算法進行垃圾回收,所以新生代10M的記憶體中,8M是Eden,1M是Survivor,另外的1M是未使用配合複製演算法的記憶體區塊,也是Survivor。

 1 public class ReflectTest { 2  3     private static final int _1MB = 1024*1024; 4      5     public static void testAllocation(){ 6         byte[] allocation1 , allocation2 , allocation3 , allocation4; 7         allocation1 = new byte[2 * _1MB]; 8         allocation2 = new byte[2 * _1MB]; 9         allocation3 = new byte[2 * _1MB];10         allocation4 = new byte[6 * _1MB];11     }12     13     public static void main(String[] args) {14         ReflectTest.testAllocation();15     }16     17 }

 輸出如下

Heap
 PSYoungGen      total 9216K, used 6651K [0x000000000b520000, 0x000000000bf20000, 0x000000000bf20000)
  eden space 8192K, 81% used [0x000000000b520000,0x000000000bb9ef28,0x000000000bd20000)
  from space 1024K, 0% used [0x000000000be20000,0x000000000be20000,0x000000000bf20000)
  to   space 1024K, 0% used [0x000000000bd20000,0x000000000bd20000,0x000000000be20000)
 PSOldGen        total 10240K, used 6144K [0x000000000ab20000, 0x000000000b520000, 0x000000000b520000)
  object space 10240K, 60% used [0x000000000ab20000,0x000000000b120018,0x000000000b520000)
 PSPermGen       total 21248K, used 2973K [0x0000000005720000, 0x0000000006be0000, 0x000000000ab20000)
  object space 21248K, 13% used [0x0000000005720000,0x0000000005a07498,0x0000000006be0000)

 可以看到eden佔用了81%,說明allocation1 , allocation2 , allocation3 都是分配在新生代Eden上。

2、大物件直接分配在老年代上

  大對像是指需要大量連續記憶體空間去存放的對象,類似於那種很長的字串和陣列。大物件對於虛擬機器的記憶體分佈來講並不是好事,當遇到很多存活僅一輪的大物件jvm更難處理,寫程式碼的時候應該避免這樣的問題。虛擬機器中提供了-XX:PretenureSizeThreshold參數,另大於這個值的物件直接分配到老年代,這樣做的目的是為了避免在Eden區和Survivor區之間發生大量的記憶體copy,在之前講過的垃圾回收演算法複製演算法有提到過,就不多說了。

public class ReflectTestBig {private static final int _1MB = 1024*1024;    public static void testAllocation(){byte[] allocation2 , allocation3 , allocation4;allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[6 * _1MB];
    }    public static void main(String[] args) {
        ReflectTestBig.testAllocation();
    }
    
}

 輸出如下

Heap
 PSYoungGen      total 8960K, used 4597K [0x000000000b510000, 0x000000000bf10000, 0x000000000bf10000)
  eden space 7680K, 59% used [0x000000000b510000,0x000000000b98d458,0x000000000bc90000)
  from space 1280K, 0% used [0x000000000bdd0000,0x000000000bdd0000,0x000000000bf10000)
  to   space 1280K, 0% used [0x000000000bc90000,0x000000000bc90000,0x000000000bdd0000)
 PSOldGen        total 10240K, used 6144K [0x000000000ab10000, 0x000000000b510000, 0x000000000b510000)
  object space 10240K, 60% used [0x000000000ab10000,0x000000000b110018,0x000000000b510000)
 PSPermGen       total 21248K, used 2973K [0x0000000005710000, 0x0000000006bd0000, 0x000000000ab10000)
  object space 21248K, 13% used [0x0000000005710000,0x00000000059f7460,0x0000000006bd0000)

 可以看到allocation4已經超過了設定的-XX:PretenureSizeThreshold=3145728,隨意allocation4直接被分配到了老年代,老年代佔用率為60%。注意這裡設定-XX:PretenureSizeThreshold=3145728不能寫成-XX:PretenureSizeThreshold=3m,否則jvm將無法辨識。

3、長期存活的物件將進入老年代

  虛擬機既然採用了分帶收集的思想來管理內存,那麼內存回收就必須識別哪些對象應該放在新生代,哪些對象應該放在老年代。為了打到目的,jvm為每個物件定義了一個年齡計數器(Age)。如果物件在Eden出生並且能過第一次Minor GC後仍然存活,並且可以在Survivor存放的話,將被移動到Survivor中,並將對象的年齡設為1。對象每躲過一次Minor GC,年齡就會加1,當他的年齡超過一年的閾值的時候,該對象就會晉升到老年代。這個閾值jvm預設是15,可以透過-XX:MaxTenuringThreshold來設定。

   m = 1024 * 1024  [] a1 =  [1 * m / 4[] a2 =  [7 *[] a3 =  [3 * m];

 輸出如下

[GC [DefNew: 7767K->403K(9216K), 0.0062209 secs] 7767K->7571K(19456K), 0.0062482 secs]   
[Times: user=0.00 sys=0.00, real=0.01 secs]   
a3 ok  
Heap  
 def new generation   total 9216K, used 3639K [0x331d0000, 0x33bd0000, 0x33bd0000)  
  eden space 8192K,  39% used [0x331d0000, 0x334f9040, 0x339d0000)  
  from space 1024K,  39% used [0x33ad0000, 0x33b34de8, 0x33bd0000)  
  to   space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)  
 tenured generation   total 10240K, used 7168K [0x33bd0000, 0x345d0000, 0x345d0000)  
   the space 10240K,  70% used [0x33bd0000, 0x342d0010, 0x342d0200, 0x345d0000)  
 compacting perm gen  total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000)  
   the space 12288K,   3% used [0x345d0000, 0x3462f548, 0x3462f600, 0x351d0000)  
    ro space 10240K,  55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000)  
    rw space 12288K,  55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000)
#

You can see that a2 has survived once, and its age is 1, which satisfies the set -XX:MaxTenuringThreshold=1, so a2 has entered the old generation, and a3 has entered the new generation.

4. Dynamic object age determination

In order to better adapt to the memory status of different programs, the virtual machine does not always require that the age of the object must reach the value set by -XX:MaxTenuringThreshold. In order to be promoted to the old age, if the sum of the sizes of all objects of the same age in the Survivor space is greater than half of the Survivor space, objects whose age is greater than or equal to this age can directly enter the old age without reaching the value set in -XX:MaxTenuringThreshold.

5. Space Allocation Guarantee

When Minor GC occurs, the virtual machine will detect whether the average size of each promotion to the old generation is greater than the remaining space of the old generation. If it is greater, proceed directly. One FUll GC. If it is less than, check whether the HandlerPromotionFailyre setting allows guarantee failure. If it is allowed, only Minor GC will be performed. If it is not allowed, a FUll GC will also be improved. That is to say, when the new generation Eden cannot store the modified object, the object will be stored in the old generation.

3. Commonly used jvm parameter settings

1. -Xms: Initial heap size, default (MinHeapFreeRatio parameter can be adjusted) When the free heap memory is less than 40%, the JVM will increase the heap until -Maximum limit of Xmx.

2.

3. -Xmn: Young generation size (1.4or lator), the size here is (eden+ 2 survivor space). It is different from the New gen shown in jmap -heap.
The entire heap size = young generation size + old generation size + persistent generation size.
After increasing the young generation, the size of the old generation will be reduced. This value has a greater impact on system performance. Sun officially recommends a configuration of 3/8 of the entire heap.

4. -XX:NewSize: Set the young generation size (for 1.3/1.4).

5. -XX:MaxNewSize: The maximum value of the young generation (for 1.3/1.4).

6, -XX:PermSize: Set the initial value of the persistent generation (perm gen).

7, -XX:MaxPermSize: Set the maximum size of the persistent generation.

8. -Xss: The stack size of each thread. After JDK5.0, the stack size of each thread is 1M. In the past, the stack size of each thread was 256K. The memory size required by the application thread can be adjusted. .Under the same physical memory, reducing this value can generate more threads. However, the operating system still has limits on the number of threads in a process and cannot be generated infinitely. The experience value is around 3000~5000.

9, -XX:NewRatio: The ratio of the young generation (including Eden and two Survivor areas) to the old generation (excluding the persistent generation), -XX:NewRatio=4 indicates the difference between the young generation and the old generation The ratio value is 1:4, and the young generation accounts for 1/5 of the entire stack. When Xms=Xmx and Xmn is set, this parameter does not need to be set.

10. -XX:SurvivorRatio: The size ratio of the Eden area and the Survivor area is set to 8, then the ratio of two Survivor areas to one Eden area is 2:8, and one Survivor area accounts for the entire young generation. 1/10.

11. -XX:LargePageSizeInBytes: The size of the memory page cannot be set too large, which will affect the size of Perm.

12. -XX:+DisableExplicitGC: Close System.gc()

13. -XX:MaxTenuringThreshold: The maximum age of garbage. If set to 0, the young generation objects will not pass through Survivor area, directly enter the old generation. For applications with a large number of old generations, efficiency can be improved. If this value is set to a larger value, the young generation objects will be copied multiple times in the Survivor area, so that more objects can be added. The survival time of the young generation increases the probability of being recycled in the young generation. This parameter is only effective in serial GC.

14. -XX:PretenureSizeThreshold: If the object exceeds the size, it is allocated directly in the old generation. The unit byte is invalid when the new generation uses Parallel Scavenge GC. Another situation where it is allocated directly in the old generation is a large array. object, and there are no external reference objects in the array.

15. -XX:TLABWasteTargetPercent: The percentage of TLAB in the eden area.

4. Supplement

The difference between Minor GC and FUll GC:

New generation GC (Minor GC): refers to the garbage collection action that occurs in the new generation, because java objects Large logarithms cannot escape the first round of GC, so Minor GC is used frequently and the recovery speed is generally faster.

Old generation GC (FULL GC/Major GC): refers to the GC that occurs in the old generation. When Major GC appears, it is often accompanied by at least one Minor GC (but not absolutely, in the collection strategy of the ParallelScavenge collector) There is a direct selection process for Major GC). The speed of Major GC is generally more than 10 times slower than Minor GC.

以上是淺談java記憶體分配與回收策略的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn