欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入了解java內(nèi)存分配和回收策略

 更新時(shí)間:2017年06月15日 09:48:06   投稿:jingxian  
下面小編就為大家?guī)硪黄钊肓私鈐ava內(nèi)存分配和回收策略。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

一、導(dǎo)論

java技術(shù)體系中所提到的內(nèi)存自動化管理歸根結(jié)底就是內(nèi)存的分配與回收兩個(gè)問題,之前已經(jīng)和大家談過java回收的相關(guān)知識,今天來和大家聊聊java對象的在內(nèi)存中的分配。通俗的講,對象的內(nèi)存分配就是在堆上的分配,對象主要分配在新生代的Eden上(關(guān)于對象在內(nèi)存上的分代在垃圾回收中會補(bǔ)上,想了解的也可以參考《深入理解java虛擬機(jī)》),如果啟動了本地線程分配緩沖,講按線程優(yōu)先在TLAB上分配。少數(shù)情況下也是直接在老年代中分配。

二、經(jīng)典的分配策略

1、對象優(yōu)先在Eden上分配

一般情況下對象都是優(yōu)先分配在Eden上,當(dāng)Eden沒有足夠的空間進(jìn)行分配時(shí),jvm會發(fā)起一次Minor GC。如果還是沒有足夠的空間分配,后面還有另外的措施,下面會提到。

設(shè)置虛擬機(jī)的偶記日志參數(shù)-XX:+PrintGCDetails,在垃圾回收的時(shí)候會打印內(nèi)存的回收日志,并且在進(jìn)程退出的時(shí)候會輸出當(dāng)前內(nèi)存各區(qū)域的分配情況。下面來看下具體的例子,首先需要設(shè)置jvm的參數(shù)-Xms20m -Xmx20m -Xmn10m,這三個(gè)參數(shù)說明java堆大小為20M,且不可擴(kuò)展,其中10M分配給新生代,剩下的10M分配給老年代。-XX:SurvivorRatio=8是jvm默認(rèn)的新生代中Eden和Survivor比例,默認(rèn)為8:1。原因是新生代中的對象98%都會在下一次GC的時(shí)候回收掉,所以很適合采用復(fù)制算法進(jìn)行垃圾回收,所以新生代10M的內(nèi)存中,8M是Eden,1M是Survivor,另外的1M是未使用配合復(fù)制算法的內(nèi)存塊,也是Survivor。

public class ReflectTest {

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

輸出如下

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、大對象直接分配在老年代上

大對象是指需要大量連續(xù)內(nèi)存空間去存放的對象,類似于那種很長的字符串和數(shù)組。大對象對于虛擬機(jī)的內(nèi)存分布來講并不是好事,當(dāng)遇到很多存活僅一輪的大對象jvm更加難處理,寫代碼的時(shí)候應(yīng)該避免這樣的問題。虛擬機(jī)中提供了-XX:PretenureSizeThreshold參數(shù),另大于這個(gè)值的對象直接分配到老年代,這樣做的目的是為了避免在Eden區(qū)和Survivor區(qū)之間發(fā)生大量的內(nèi)存copy,在之前講過的垃圾回收算法復(fù)制算法有提到過,就不多說了。

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已經(jīng)超過了設(shè)置的-XX:PretenureSizeThreshold=3145728,隨意allocation4直接被分配到了老年代,老年代占用率為60%。注意這里設(shè)置-XX:PretenureSizeThreshold=3145728不能寫成-XX:PretenureSizeThreshold=3m,否則jvm將無法識別。

3、長期存活的對象將進(jìn)入老年代

虛擬機(jī)既然采用了分帶收集的思想來管理內(nèi)存,那內(nèi)存回收就必須識別哪些對象應(yīng)該放在新生代,哪些對象應(yīng)該放在老年代。為了打到目的,jvm給每個(gè)對象定義了一個(gè)年齡計(jì)數(shù)器(Age)。如果對象在Eden出生并且能過第一次Minor GC后仍然存活,并且可以在Survivor存放的話,將被移動到Survivor中,并將對象的年齡設(shè)為1。對象每躲過一次Minor GC,年齡就會加1,當(dāng)他的年齡超過一年的閾值的時(shí)候,該對象就會晉升到老年代。這個(gè)閾值jvm默認(rèn)是15,可以通過-XX:MaxTenuringThreshold來設(shè)置。

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已經(jīng)存活了一次,年齡為1,滿足所設(shè)置的-XX:MaxTenuringThreshold=1,所以a2進(jìn)入了老年代,而a3則進(jìn)入了新生代。

4、動態(tài)對象年齡判定

為了能更好的適應(yīng)不同程序的內(nèi)存狀態(tài),虛擬機(jī)并不總是要求對象的年齡必須達(dá)到-XX:MaxTenuringThreshold所設(shè)置的值才能晉升到老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年區(qū),無須達(dá)到-XX:MaxTenuringThreshold中的設(shè)置值。

5、空間分配擔(dān)保

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

三、常用的jvm參數(shù)設(shè)置

1、-Xms: 初始堆大小, 默認(rèn)(MinHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存小于40%時(shí),JVM就會增大堆直到-Xmx的最大限制。

2、Xmx: 最大堆大小,默認(rèn)(MaxHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存大于70%時(shí),JVM會減少堆直到 -Xms的最小限制。

3、-Xmn: 年輕代大小(1.4or lator), 此處的大小是(eden+ 2 survivor space).與jmap -heap中顯示的New gen是不同的。

整個(gè)堆大小=年輕代大小 + 年老代大小 + 持久代大小。

增大年輕代后,將會減小年老代大小.此值對系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8。

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

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

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

7、-XX:MaxPermSize: 設(shè)置持久代最大值。

8、-Xss: 每個(gè)線程的堆棧大小,JDK5.0以后每個(gè)線程堆棧大小為1M,以前每個(gè)線程堆棧大小為256K.更具應(yīng)用的線程所需內(nèi)存大小進(jìn)行 調(diào)整.在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程.但是操作系統(tǒng)對一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無限生成,經(jīng)驗(yàn)值在3000~5000左右。

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

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

11、-XX:LargePageSizeInBytes: 內(nèi)存頁的大小不可設(shè)置過大, 會影響Perm的大小。

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

13、-XX:MaxTenuringThreshold: 垃圾最大年齡,如果設(shè)置為0的話,則年輕代對象不經(jīng)過Survivor區(qū),直接進(jìn)入年老代. 對于年老代比較多的應(yīng)用,可以提高效率.如果將此值設(shè)置為一個(gè)較大值,則年輕代對象會在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對象再年輕代的存活 時(shí)間,增加在年輕代即被回收的概率該參數(shù)只有在串行GC時(shí)才有效。

14、-XX:PretenureSizeThreshold: 對象超過多大是直接在舊生代分配,單位字節(jié) 新生代采用Parallel Scavenge GC時(shí)無效另一種直接在舊生代分配的情況是大的數(shù)組對象,且數(shù)組中無外部引用對象。

15、-XX:TLABWasteTargetPercent: TLAB占eden區(qū)的百分比。

四、補(bǔ)充

Minor GC和FUll GC的區(qū)別:

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

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

以上這篇深入了解java內(nèi)存分配和回收策略就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java 基礎(chǔ):string中的compareTo方法

    Java 基礎(chǔ):string中的compareTo方法

    這篇文章主要介紹了Java 基礎(chǔ):string中的compareTo方法,文章圍繞string中的compareTo方法的相關(guān)資料展開文章詳細(xì)內(nèi)容,希望對待大家有所幫助
    2021-12-12
  • Myeclipse清理項(xiàng)目緩存的幾大方法

    Myeclipse清理項(xiàng)目緩存的幾大方法

    今天小編就為大家分享一篇關(guān)于Myeclipse清理項(xiàng)目緩存的幾大方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • Java行為型設(shè)計(jì)模式之策略模式詳解

    Java行為型設(shè)計(jì)模式之策略模式詳解

    策略模式屬于Java-設(shè)計(jì)模式中行為模式之一,該模式定義了一系列算法,并將每個(gè)算法封裝起來,使它們可以相互替換。本文將通過示例詳細(xì)講解這一模式,需要的可以參考一下
    2022-11-11
  • 重學(xué)SpringBoot3之日志Logging使用方式

    重學(xué)SpringBoot3之日志Logging使用方式

    在日常開發(fā)中會遇到不同的異常,日志方便我們?nèi)ヅ挪樘幚?這篇文章主要給大家介紹了關(guān)于重學(xué)SpringBoot3之日志Logging使用方式的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • Java有序鏈表的合并實(shí)現(xiàn)方法

    Java有序鏈表的合并實(shí)現(xiàn)方法

    這篇文章主要通過兩個(gè)例題為大家介紹一下Java合并兩個(gè)及以上有序鏈表的實(shí)現(xiàn)方法,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以參考一下
    2023-04-04
  • java實(shí)現(xiàn)英文翻譯程序

    java實(shí)現(xiàn)英文翻譯程序

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)英文翻譯程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • Java關(guān)于List集合去重方案詳細(xì)介紹

    Java關(guān)于List集合去重方案詳細(xì)介紹

    實(shí)際項(xiàng)目開發(fā)中,很多業(yè)務(wù)場景下都會遇見集合去重。在說到List集合去重之前,首先我們回顧下普通類型的list如何去重
    2021-09-09
  • Java生成pdf文件或jpg圖片的案例講解

    Java生成pdf文件或jpg圖片的案例講解

    這篇文章主要介紹了Java生成pdf文件或jpg圖片的案例講解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • SpringBoot封裝JDBC的實(shí)現(xiàn)步驟

    SpringBoot封裝JDBC的實(shí)現(xiàn)步驟

    本文主要介紹了SpringBoot封裝JDBC的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Java使用LinkedHashMap進(jìn)行分?jǐn)?shù)排序

    Java使用LinkedHashMap進(jìn)行分?jǐn)?shù)排序

    這篇文章主要介紹了Java使用LinkedHashMap進(jìn)行分?jǐn)?shù)排序的相關(guān)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05

最新評論