一.堆的概述
一個JVM例項只有一個堆記憶體,堆也是Java記憶體管理的核心區域,堆在JVM啟動的時候建立,其空間大小也被建立,是JVM中最大的一塊記憶體空間,所有執行緒共享Java堆,物理上不連續的邏輯上連續的記憶體空間,幾乎所有的例項都在這裡分配記憶體,在方法結束後,堆中的物件不會馬上刪除,僅僅在垃圾收集的時候被刪除,堆是GC(垃圾收集器)執行垃圾回收的重點區域。
二.堆空間細分
Java7及以前將堆空間邏輯上分成三部分:新生區+養老區+永久代
Java8及以後將堆記憶體邏輯上分為:新生區+養老區+元空間
新生代:
1.新生代使用了複製演算法
2.新生代為gc的重點物件,經官方測試70%物件都生命週期都會在新生代中完結
3.新生代又分為了eden、survivor1、survivor2,物件建立先放在eden中,經過一定時間還倖存就會放在倖存者區
4.記憶體比例分預設為:8:1:1
5.新生代收集器:Minor GC/Young GC
eden(新生區)
當初始載入物件時會進入新生區
survivor(倖存區)
倖存區又分為from 和 to —誰為空誰為to ,始終都會有一個區域為空。
倖存區不會主動進行垃圾回收,只會eden回收時才會附帶進行gc
當在倖存區中的閾值達到了15後(預設15可修改)會自動進入老年代
當新生區(eden)出現了記憶體不足時,會進行YGC,那麼會將沒有指標的物件回收,還有指標引向的物件放入survivor1或者survivor2區域中,eden清空,資料放入一個survivor中。—當第二次進行gc那麼會將eden資料放入另一個空的survivor中,並且將當前survivor中有效資料,放入空的survivor中,一次類推。
老年代
1.較大的物件資料會放入老年代
2.年代的資料都是相對於持久的不會頻繁的gc
3.(MajorGC / Old GC) 在進行majorgc時會至少進行一次minorGc ,而且majorgc的效率是比minorGc 慢10倍的
4.老年代收集器:MajorGC / Old GC 要區分與Full GC
在一個物件進入記憶體時 會進入eden,如果滿了(YGC進行回收沒有引用的,如果還有引用的)會放入s1或者s0這就涉及到to from哪個為空就是to,(下次eden再次滿了會將有資料的【舉例s1】中 的資料放入s0,並且進行迭代版本)以此類推,當某個物件迭代閾值的次數達到預設15此後,(當然也會有特殊的優化:如當survivor區域中相同年齡的記憶體總和大於survivor的一半記憶體,會將大於等於平均年齡的物件提前放入老年代)會放入老年代 關於YGC 全程(YoungGC) 也可以為(Minor GC) s1,0是不會有單獨的gc回收只會被動的依賴於eden的gc當eden進行gc時會自動回收s1,s0
yangGC只會在eden區滿的時候進行,不會在survivor區滿的時候進行,eden區GC時也會把survivor區進行GC,當survivor中age=15時會將資料放入老年區。
三為什麼分代
四記憶體分配策略(或物件提升規則)
如果物件在EDEN出生並且經過一次MinnorGC後依然存活,並且能被Survivor容納的話,將會被放在倖存者區,並將物件年齡設為1,每熬過一次MinnorGC,age增加一歲,當age增加到15時,會被放入老年代
五.TLAB
六逃逸分析
什麼是逃逸?
也就是如果在方法內建立物件,並且return進行傳出,或者賦值到外部的變數,那麼就進行了逃逸。
-XX:+DoEscapeAnalysis (JDK1.8之後預設開啟)
-XX:+DoEscapeAnalysis(關閉)
逃逸分析包括以下
棧上分配
也就是將物件直接分配到棧上,跟隨棧的消亡而消亡,減少了gc(棧中沒有gc),提高了效能、速度。
同步省略
因為是每個棧獨有的,一個棧也就是一個執行緒所以不存在同步安全的問題。
分離物件或者標量替換
擴充:一個類代表一個:聚合量,標量是無法分析的最小資料,聚合量可以分析為標量,也就是分析屬性
也就是當載入一個pojo類時,不會建立物件而是,標量替換進行分析成一個個小的屬性,減少了記憶體,提高了效能。
但是基於hotSpot 虛擬機器這項技術並不成熟,因為還需要進行判斷是否 屬於逃逸,如果沒有逃逸,可能會浪費了判斷的時間等一些問題。
但是最後標量替換還是引用到了hotSpot虛擬機器中
所以問題—所有的物件都是儲存在堆空間中麼?
回答:是的
總結
1.爭對倖存者S0和S1區的總結:複製之後有交換,誰跟誰是to
2.關於垃圾回收:頻繁在新生代中收集,很少在養老區收集,幾乎不在永久代/元空間中收集
3.物件的執行流程