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

来源:http://www.cnblogs.com/mxlandxt/archive/2017/06/14/7009825.html
-Advertisement-
Play Games

一、導論 java技術體系中所提到的記憶體自動化管理歸根結底就是記憶體的分配與回收兩個問題,之前已經和大家談過java回收的相關知識,今天來和大家聊聊java對象的在記憶體中的分配。通俗的講,對象的記憶體分配就是在堆上的分配,對象主要分配在新生代的Eden上(關於對象在記憶體上的分代在垃圾回收中會補上,想瞭解 ...


一、導論

  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來設置。

public class JavaTest {  
  
    static int m = 1024 * 1024;  
  
    public static void main(String[] args) {  
        byte[] a1 = new byte[1 * m / 4];  

     byte[] a2 = new byte[7 * m];

     byte[] a3 = new byte[3 * m]; //GC } }

 輸出如下

[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) 

 可以看到a2已經存活了一次,年齡為1,滿足所設置的-XX:MaxTenuringThreshold=1,所以a2進入了老年代,而a3則進入了新生代。

4、動態對象年齡判定

  為了能更好的適應不同程式的記憶體狀態,虛擬機並不總是要求對象的年齡必須達到-XX:MaxTenuringThreshold所設置的值才能晉升到老年代,如果在Survivor空間中相同年齡所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年區,無須達到-XX:MaxTenuringThreshold中的設置值。

5、空間分配擔保

  在發生Minor GC的時候,虛擬機會檢測每次晉升到老年代的平均大小是否大於老年代的剩餘空間,如果大於,則直接進行一次FUll GC。如果小於,則查看HandlerPromotionFailyre設置是否允許擔保失敗,如果允許那就只進行Minor GC,如果不允許則也要改進一次FUll GC。也就是說新生代Eden存不下改對象的時候就會將該對象存放在老年代。

三、常用的jvm參數設置

1、-Xms: 初始堆大小, 預設(MinHeapFreeRatio參數可以調整)空餘堆記憶體小於40%時,JVM就會增大堆直到-Xmx的最大限制。

2、Xmx: 最大堆大小,預設(MaxHeapFreeRatio參數可以調整)空餘堆記憶體大於70%時,JVM會減少堆直到 -Xms的最小限制。

3、-Xmn: 年輕代大小(1.4or lator), 此處的大小是(eden+ 2 survivor space).與jmap -heap中顯示的New gen是不同的。
  整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。
  增大年輕代後,將會減小年老代大小.此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。

4、-XX:NewSize: 設置年輕代大小(for 1.3/1.4)。

5、-XX:MaxNewSize: 年輕代最大值(for 1.3/1.4)。

6、-XX:PermSize: 設置持久代(perm gen)初始值。

7、-XX:MaxPermSize: 設置持久代最大值。

8、-Xss: 每個線程的堆棧大小,JDK5.0以後每個線程堆棧大小為1M,以前每個線程堆棧大小為256K.更具應用的線程所需記憶體大小進行 調整.在相同物理記憶體下,減小這個值能生成更多的線程.但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。

9、-XX:NewRatio: 年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代),-XX:NewRatio=4表示年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5。Xms=Xmx並且設置了Xmn的情況下,該參數不需要進行設置。

10、-XX:SurvivorRatio: Eden區與Survivor區的大小比值,設置為8,則兩個Survivor區與一個Eden區的比值為2:8,一個Survivor區占整個年輕代的1/10。

11、-XX:LargePageSizeInBytes: 記憶體頁的大小不可設置過大, 會影響Perm的大小。

12、-XX:+DisableExplicitGC: 關閉System.gc()

13、-XX:MaxTenuringThreshold: 垃圾最大年齡,如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代. 對於年老代比較多的應用,可以提高效率.如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次複製,這樣可以增加對象再年輕代的存活 時間,增加在年輕代即被回收的概率該參數只有在串列GC時才有效。

14、-XX:PretenureSizeThreshold: 對象超過多大是直接在舊生代分配,單位位元組 新生代採用Parallel Scavenge GC時無效另一種直接在舊生代分配的情況是大的數組對象,且數組中無外部引用對象。

15、-XX:TLABWasteTargetPercent: TLAB占eden區的百分比。

四、補充

Minor GC和FUll GC的區別:

  新生代GC(Minor GC):指發生在新生代的垃圾收集動作,因為java對象大對數都是逃不過第一輪的GC,所以Minor GC使用很頻繁,一般回收速度也比較快。

  老年代GC(FULL GC/Major GC) :指發生在老年代的GC,出現了Major GC,經常會伴隨至少一次的Minor GC(但非絕對,在ParallelScavenge收集器的收集策略中就有直接進行Major GC的選擇過程 )。Major GC的速度一般會比Minor GC慢10倍以上。 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1,ref 和out 的區別https://i.cnblogs.com/EditPosts.aspx?postid=7009441&update=1 2,有三個a 標簽 沒有id,沒有name,沒有class 如何選中第二個 a 標簽,用jquert 選擇 <a href ="#這是第一個" </a ...
  • 工具包及demo: 鏈接: https://pan.baidu.com/s/1i4TUUep 密碼: gssq ...
  • 引用類型 構造器是將類型的實例初始化為良好狀態的特殊方法,創建引用類型的實例時,首先為實例的數據欄位分配記憶體,然後初始化對象的附加欄位(類型對象指針和同步塊索引),最後調用類型的實例構造器來設置對象的初始化狀態。 構造引用類型對象時,在電泳類型的實例構造器之前,為對象分配的記憶體總是先被歸0,沒有被構 ...
  • 炎炎夏日將至,近乎新版v5.0正式發佈! 本次近乎全新改版,從最初的定位到最終的呈現方式,v5.0都以嶄新面孔示人。無論是追求高質高感的加“料”動作,靈活百變的“型”體塑造,還是性能的高調升級,v5.0都在極力探索互聯網下企業社交之道,以快速搭建和定製出符合客戶需求的新型社區網站為目標,實現多終端內 ...
  • 多年前我自學的時候是很茫然,上網問問題,總是一堆外行的人說很難啊,你需要這樣需要那樣,不然就是,一堆人說一些空話,多看多寫,買好書,我很無語,除了這些就沒有自己的一些想法嗎? 首先很多人認為學JAVA要C的基礎,但是實際上不用,學開車,我不想先學騎自行車,沒有必要。 第一階段: 1. JAVA語法和 ...
  • 閱讀目錄 建議65:避開基本類型數組轉換列表陷阱 建議66:asList方法產生的List的對象不可更改 建議67:不同的列表選擇不同的遍歷演算法 建議68:頻繁插入和刪除時使用LinkList 建議69:列表相等只關心元素數據 閱讀目錄 建議65:避開基本類型數組轉換列表陷阱 建議66:asList ...
  • 在引言章節里,介紹了MovieLens 1M數據集的處理示例。書中介紹該數據集來自GroupLens Research(http://www.groupLens.org/node/73),該地址會直接跳轉到https://grouplens.org/datasets/movielens/,這裡面提供 ...
  • 迴圈(Loops) For條件遞增語句 語法是這樣的:用for作為迴圈的開始,告訴Xcode你要聲明一個迴圈了,for後面跟著括弧,括弧裡面聲明變數、條件和遞增數值。例如: 括弧中的第一個部分是變數,用counter表示,計算已經完成的迴圈的數量,在平時編寫程式時,這裡的變數常常命名為counter ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...