JVM內存分配策略

-------------------------------------------------------------------------------JVM內存分配策略-----------------------------------------------------------------------------------------

一:對象內存分配兩種方法

爲對象分配空間的任務等同於把一塊肯定大小的內存從Java堆中劃分出來。java

  • 指針碰撞(Serial、ParNew等帶Compact過程的收集器) 假設Java堆中內存是絕對規整的,全部用過的內存都放在一邊,空閒的內存放在另外一邊,中間放着一個指針做爲分界點的指示器,那所分配內存就僅僅是把那個指針向空閒空間那邊挪動一段與對象大小相等的距離,這種分配方式稱爲「指針碰撞」(Bump the Pointer)。
  • 空閒列表(CMS這種基於Mark-Sweep算法的收集器) 若是Java堆中的內存並非規整的,已使用的內存和空閒的內存相互交錯,那就沒有辦法簡單地進行指針碰撞了,虛擬機就必須維護一個列表,記錄上哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,並更新列表上的記錄,這種分配方式稱爲「空閒列表」(Free List)。算法

選擇哪一種分配方式由Java堆是否規整決定,而Java堆是否規整又由所採用的垃圾收集器是否帶有壓縮整理功能決定。所以,在使用Serial、ParNew等帶Compact過程的收集器時,系統採用的分配算法是指針碰撞,而使用CMS這種基於Mark-Sweep算法的收集器時,一般採用空閒列表。安全

 

二:Java對象分配流程

三:棧上分配

     咱們經過JVM內存分配能夠知道JAVA中的對象都是在堆上進行分配,當對象沒有被引用的時候,須要依靠GC進行回收內存,若是對象數量較多的時候,會給GC帶來較大壓力,也間接影響了應用的性能。爲了減小臨時對象在堆內分配的數量,JVM經過逃逸分析肯定該對象不會被外部訪問。那就經過標量替換將該對象分解在棧上分配內存,這樣該對象所佔用的內存空間就能夠隨棧幀出棧而銷燬,就減輕了垃圾回收的壓力。
oracle

       逃逸分析:逃逸分析是編譯語言中的一種優化分析,而不是一種優化的手段。經過對象的做用範圍的分析,爲其餘優化手段提供分析數據從而進行優化。包括全局變量賦值逃逸,方法返回值逃逸,實例引用發生逃逸,線程逃逸:賦值給類變量或能夠在其餘線程中訪問的實例變量。工具

       標量替換:標量即不可被進一步分解的量,而JAVA的基本數據類型就是標量(如:int,long等基本數據類型以及reference類型等),標量的對立就是能夠被進一步分解的量,而這種量稱之爲聚合量。而在JAVA中對象就是能夠被進一步分解的聚合量。經過逃逸分析肯定該對象不會被外部訪問,而且對象能夠被進一步分解時,JVM不會建立該對象,而會將該對象成員變量分解若干個被這個方法使用的成員變量所代替。這些代替的成員變量在棧幀或寄存器上分配空間。性能

   

四:TLAB分配

五:內存分配規則

     (使用Serial/Serial Old收集器)測試

        一:對象優先分配在Eden區:大多數狀況下,對象在新生代中的Eden區分配,當Eden區沒有足夠空間進行分配時,虛擬機將發生一次Minor GC。優化

        二:大對象直接進入老年代:大對象是指,須要大量連續內存空間的Java對象,虛擬機提供了相關參數調整大小。this

        三:長期存活的對象進入老年代:每次Minor GC,年齡就增長一歲,默認15歲,進入老年代,也能夠經過參數調整。url

        四:動態對象年齡斷定:若是在Survivor空間中相同年齡全部對象大小的總和大小大於Survivor空間的一半,年齡大於或等於該年齡的對象就能夠直接進入老年代。

        五:空間分配擔保:在發生Minor GC前,虛擬機會檢查老年代的最大可用連續空間是否大於新生代全部對象的總空間,成立安全,不成立,觸發Full GC。

 

六:測試Demo

AllocDemo.java),工具IDEA,以下圖配置VM參數

public class AllocDemo { class User { int id; String name; User(int id, String name) { this.id = id; this.name = name; } } private void alloc(int i) { User user = new User(i, "name" + i); } public static void main(String[] args) { AllocDemo a = new AllocDemo(); long s1 = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { a.alloc(i); } long s2 = System.currentTimeMillis(); System.out.println("time:"+(s2 - s1));//記錄分配時間 } }

 

 

  關閉逃逸分析,關閉標量替換,關閉TLAB分配        設置參數: -XX:-DoEscapeAnalysis  -XX:-EliminateAllocations -XX:-UseTLAB  -XX:+PrintGCDetails

 

 關閉逃逸分析,關閉標量替換,開啓TLAB分配      設置參數:-XX:-DoEscapeAnalysis  -XX:-EliminateAllocations -XX:+UseTLAB  -XX:+PrintGCDetails

開啓逃逸分析,開啓標量替換,開啓TLAB分配       設置:-XX:+DoEscapeAnalysis  -XX:+EliminateAllocations -XX:+UseTLAB  -XX:+PrintGCDetails

    若是想看java堆上的對象分佈狀況

    1.經過jps查看Class的Main進程的PID 

    2. jmap -histo [pid]查看java堆上的對象分佈狀況 。||  jmap -dump:format=b,file=文件名.hprof pid生成dump文件

    若是不熟悉jps,jmap等用法的能夠參考官網:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/

 

   內容主要來源:《深刻理解Java虛擬機》

相關文章
相關標籤/搜索