淺談java記憶體分配與回收策略
一、導論
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中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

本站9月3日消息,韓媒etnews當地時間昨報道稱,三星電子和SK海力士的「類HBM式」堆疊結構行動記憶體產品將在2026年後實現商業化。消息人士表示這兩大韓國記憶體巨頭將堆疊式行動記憶體視為未來重要收入來源,並計劃將「類HBM記憶體」擴展到智慧型手機、平板電腦和筆記型電腦中,為端側AI提供動力。綜合本站先前報導,三星電子的此類產品叫做LPWideI/O內存,SK海力士則將這方面技術稱為VFO。兩家企業使用了大致相同的技術路線,即將扇出封裝和垂直通道結合在一起。三星電子的LPWideI/O內存位寬達512

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4
