一、 JVM學(xué)習(xí)筆記
JVM:Java Virtual Machine
JVM總感覺(jué)那么的神秘,底層的東西,試著把一些復(fù)雜的概念,用簡(jiǎn)單的方式做一個(gè)總覺(jué)。
那么學(xué)習(xí)JVM主要是學(xué)習(xí)哪些內(nèi)容。
1.運(yùn)行時(shí)數(shù)據(jù)區(qū)。
2.堆模型。
3.垃圾回收算法。
4.引用分類。
JVM在開(kāi)發(fā)中的位置:
1.1、運(yùn)行時(shí)數(shù)據(jù)區(qū):
- 程序計(jì)數(shù)器program Counter Register:
線程私有,當(dāng)前線程。
指向當(dāng)前線程正在執(zhí)行的字節(jié)碼指令地址。每條現(xiàn)成都有一個(gè)獨(dú)立的程序計(jì)數(shù)器,互不影響、獨(dú)立存儲(chǔ)。字節(jié)碼解釋器通過(guò)改變這個(gè)計(jì)數(shù)器來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程回復(fù)都需要依賴它。 - 虛擬機(jī)棧 VM Stack:
線程私有,當(dāng)前線程執(zhí)行方法。- 每個(gè)方法都包含:局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接(運(yùn)行時(shí)多態(tài))、出口(去哪里-正常、非正常)
- StackOverflowError,如果線程請(qǐng)求的棧深度大于虛擬機(jī)允許的深度時(shí)。
- OutOfMemoryError,如果虛擬機(jī)棧進(jìn)行擴(kuò)展無(wú)法申請(qǐng)到足夠的內(nèi)存時(shí)。
- 本地方法棧 Native Method Stack:C++、C提供的功能。
- StackOverflowError
- OutOfMemoryError
- 方法區(qū) Method Area(永久代):類信息、常量final、靜態(tài)變量static、JIT。
- OutOfMemoryError,申請(qǐng)不到內(nèi)存時(shí)。
- 堆 Heap:所有線程共享,存放對(duì)象實(shí)例。
- 通過(guò)-Xmx 和 -Xms 控制
- OutOfMemoryError,當(dāng)堆無(wú)法申請(qǐng)到內(nèi)存時(shí)。
1.2、堆模型
年輕代Young Generation
一般都是朝生夕死的對(duì)象,大約80%以上,采用復(fù)制算法進(jìn)行垃圾回收。(復(fù)制算法的基本思想就是將內(nèi)存分為兩塊,每次只用其中一塊,當(dāng)這一塊內(nèi)存用完,就將還活著的對(duì)象復(fù)制到另外一塊上面。復(fù)制算法不會(huì)產(chǎn)生內(nèi)存碎片)
年輕代分為:1個(gè)eden['i:d?n]、兩個(gè)survivor s?'va?v?。默認(rèn)比例為Eden:Survivor0:Survivor1=8:1:1。survivor區(qū)的意義在于它進(jìn)行篩選進(jìn)入老年代的對(duì)象,減少被送到老年代對(duì)象的數(shù)量,進(jìn)而減少FullGC的開(kāi)銷(xiāo)。兩個(gè)survivor區(qū)主要解決內(nèi)存碎片化,也是使用復(fù)制算法的前提。
新創(chuàng)建的的對(duì)象會(huì)分配到eden區(qū)(大對(duì)象可能會(huì)特殊)。進(jìn)行MinorGC以后,在eden區(qū)中存過(guò)下來(lái)的對(duì)象會(huì)移動(dòng)到Survivor的to區(qū),在Survivor的from區(qū)的存活的對(duì)象根據(jù)年齡(一次GC漲一歲)進(jìn)行判斷,達(dá)到臨界值(可以通過(guò)-XX:MaxTenuringThreshold來(lái)設(shè)置) 會(huì)被復(fù)制到老年代,未達(dá)到的復(fù)制到To區(qū)。經(jīng)過(guò)這次GC以后eden區(qū)和From區(qū)就會(huì)被清空。這個(gè)時(shí)候From和To交換角色。此時(shí)Eden區(qū)和To區(qū)都是空的,等待下一輪GC。一直等待To區(qū)填滿,就會(huì)把這些對(duì)象移動(dòng)到老年代。
年輕代GC(MinorGC),非常頻繁,回收速度也比較快。GC算法:復(fù)制算法。
老年代Old Generation
在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對(duì)象,就會(huì)被放到老年代中。因此,可以認(rèn)為老年代中存放的都是一些生命周期較長(zhǎng)的對(duì)象。
老年代GC(Major GC/Full GC),比新生代慢10倍。GC算法:標(biāo)記清楚或標(biāo)記整理。
永久代(持久代)Permanent Generation:
用于存放靜態(tài)文件,如今Java類、方法等。
永久代對(duì)垃圾回收沒(méi)有顯著影響,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class,例如Spring Hibernate 等,在這種時(shí)候需要設(shè)置一個(gè)比較大的永久代空間來(lái)存放這些運(yùn)行過(guò)程中新增的類。
永久代大小通過(guò)-XX:MaxPermSize=<N>進(jìn)行設(shè)置。
永久帶也稱為方法區(qū)。
永久代在java8以后的HotSpot將被廢棄,原因主要有兩個(gè):
- 由于永久代內(nèi)存經(jīng)常不夠用或發(fā)生內(nèi)存泄露,引發(fā)惱人的java.lang.OutOfMemoryError: PermGen (在Java Web開(kāi)發(fā)中非常常見(jiàn))。
- 移除永久代可以促進(jìn)HotSpot JVM與 JRockit VM的融合,因?yàn)镴Rockit沒(méi)有永久代。
根據(jù)上面的各種原因,永久代最終被移除,方法區(qū)移至Metaspace,字符串常量移至Java Heap。JDK 8開(kāi)始把類的元數(shù)據(jù)放到本地化的堆內(nèi)存(native heap)中,這一塊區(qū)域就叫Metaspace,中文名叫元空間。
元空間
JDK 8開(kāi)始把類的元數(shù)據(jù)放到本地化的堆內(nèi)存(native heap)中,這一塊區(qū)域就叫Metaspace,中文名叫元空間。
最直接的表現(xiàn)就是java.lang.OutOfMemoryError:
PermGen空間問(wèn)題將不復(fù)存在,因?yàn)槟J(rèn)的類的元數(shù)據(jù)分配只受本地內(nèi)存大小的限制,也就是說(shuō)本地內(nèi)存剩余多少,這解決了空間不足的問(wèn)題。不過(guò),讓Metaspace變得無(wú)限大顯然是不現(xiàn)實(shí)的,因此我們也要限制Metaspace的大?。菏褂?XX:MaxMetaspaceSize參數(shù)來(lái)指定Metaspace區(qū)域的大小。JVM默認(rèn)在運(yùn)行時(shí)根據(jù)需要?jiǎng)討B(tài)地設(shè)置MaxMetaspaceSize的大小。
如果Metaspace的空間占用達(dá)到了設(shè)定的最大值,那么就會(huì)觸發(fā)GC來(lái)收集死亡對(duì)象和類的加載器。根據(jù)JDK 8的特性,G1和CMS都會(huì)很好地收集Metaspace區(qū)
新增參數(shù)
- -XX:MetaspaceSize是分配給類元數(shù)據(jù)空間(以字節(jié)計(jì))的初始大小(Oracle邏輯存儲(chǔ)上的初始高水位,the initial high-water-mark),此值為估計(jì)值。MetaspaceSize的值設(shè)置的過(guò)大會(huì)延長(zhǎng)垃圾回收時(shí)間。垃圾回收過(guò)后,引起下一次垃圾回收的類元數(shù)據(jù)空間的大小可能會(huì)變大。
- -XX:MaxMetaspaceSize是分配給類元數(shù)據(jù)空間的最大值,超過(guò)此值就會(huì)觸發(fā)Full GC,此值默認(rèn)沒(méi)有限制,但應(yīng)取決于系統(tǒng)內(nèi)存的大小。JVM會(huì)動(dòng)態(tài)地改變此值。
- -XX:MinMetaspaceFreeRatio表示一次GC以后,為了避免增加元數(shù)據(jù)空間的大小,空閑的類元數(shù)據(jù)的容量的最小比例,不夠就會(huì)導(dǎo)致垃圾回收。
- -XX:MaxMetaspaceFreeRatio表示一次GC以后,為了避免增加元數(shù)據(jù)空間的大小,空閑的類元數(shù)據(jù)的容量的最大比例,不夠就會(huì)導(dǎo)致垃圾回收。
1.3 垃圾回收
-
引用計(jì)數(shù)法Reference Counting
- 實(shí)現(xiàn)簡(jiǎn)單、效率高,適合簡(jiǎn)單場(chǎng)景。Flash、Python使用.
- 很難解決循環(huán)引用問(wèn)題。
標(biāo)記-清除法
復(fù)制算法
標(biāo)記-整理算法
分代收集算法
新生代-復(fù)制算法(Scavenge)
通過(guò)Eden和兩個(gè)Survivor區(qū)的復(fù)制,可避免碎片化。-
老生代-標(biāo)記清楚或標(biāo)記整理CMS(Concurrent MarkSweep)
- 初始標(biāo)記,需要Stop The World
- 并發(fā)標(biāo)記,需要Stop The World
- 重新標(biāo)記
- 并發(fā)清除
當(dāng)找不到足夠的連續(xù)控件分配會(huì)進(jìn)行一次Full GC
1.4 引用類型
- 強(qiáng)引用:不會(huì)被回收。
Object o=new Object(); // 強(qiáng)引用
- 軟引用:非必須的對(duì)象。當(dāng)內(nèi)存足夠不會(huì)被回收,當(dāng)內(nèi)存不足,就會(huì)被回收??梢员怀绦蚴褂谩J褂妙恓ava.lang.ref.SoftReference定義
`
String str=new String("abc");// 強(qiáng)引用
SoftReference<String> softRef=new SoftReference<String>(str); // 軟引用 Browser prev = new Browser();// 獲取頁(yè)面進(jìn)行瀏覽
SoftReference sr = new SoftReference(prev); // 瀏覽完畢后置為軟引用
if(sr.get()!=null) {
rev = (Browser) sr.get();
// 還沒(méi)有被回收器回收,直接獲取
} else {
prev = new Browser();
// 由于內(nèi)存吃緊,所以對(duì)軟引用的對(duì)象回收了
sr = new SoftReference(prev); // 重新構(gòu)建
}
- 弱引用:非必須對(duì)象,只能生存到下一次垃圾收集發(fā)生之前。可以獲取對(duì)象。
String str=new String("abc");
WeakReference<String> abcWeakRef = new WeakReference<String>(str);
- 虛引用:幽靈引用、幻影引用。不會(huì)對(duì)對(duì)象生存時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用取得一個(gè)對(duì)象的實(shí)例。唯一目的是在背收集器回收時(shí)受到一個(gè)系統(tǒng)通
知。