摘要:新生代收集器,復(fù)制算法,并行收集,面向吞吐量要求吞吐量?jī)?yōu)先收集器。吞吐量用戶代碼運(yùn)行時(shí)間用戶代碼運(yùn)行時(shí)間垃圾回收時(shí)間控制最大垃圾收集停頓時(shí)間,大于零的毫秒數(shù)。吞吐量大小,到的整數(shù),垃圾收集時(shí)間占總時(shí)間的比例,計(jì)算時(shí)間占用比例。
基礎(chǔ)背景 運(yùn)行時(shí)數(shù)據(jù)區(qū)域
程序計(jì)數(shù)器:
因?yàn)榫€程會(huì)切換,因此每個(gè)線程獨(dú)有一份,用作在執(zhí)行過程中記錄編譯后的class文件行號(hào).
虛擬機(jī)棧:以棧幀為單位存放局部變量.
Native方法棧:和虛擬機(jī)棧類似,不過,一個(gè)本地方法是這樣一個(gè)方法:該方法的實(shí)現(xiàn)由非java語(yǔ)言實(shí)現(xiàn),比如C語(yǔ)言實(shí)現(xiàn)。很多其它的編程語(yǔ)言都有這一機(jī)制,比如在C++中,你可以告知C++編譯器去調(diào)用一個(gè)C語(yǔ)言編寫的方法.我們知道,當(dāng)一個(gè)類第一次被使用到時(shí),這個(gè)類的字節(jié)碼會(huì)被加載到內(nèi)存,并且只會(huì)回載一次。在這個(gè)被加載的字節(jié)碼的入口維持著一個(gè)該類所有方法描述符的list,這些方法描述符包含這樣一些信息:方法代碼存于何處,它有哪些參數(shù),方法的描述符(public之類)等等。如果一個(gè)方法描述符內(nèi)有native,這個(gè)描述符塊將有一個(gè)指向該方法的實(shí)現(xiàn)的指針。這些實(shí)現(xiàn)在一些DLL文件內(nèi),但是它們會(huì)被操作系統(tǒng)加載到j(luò)ava程序的地址空間。當(dāng)一個(gè)帶有本地方法的類被加載時(shí),其相關(guān)的DLL并未被加載,因此指向方法實(shí)現(xiàn)的指針并不會(huì)被設(shè)置。當(dāng)本地方法被調(diào)用之前,這些DLL才會(huì)被加載,這是通過調(diào)用java.system.loadLibrary()實(shí)現(xiàn)的。
方法區(qū):運(yùn)行時(shí)常量池,存放編譯器的字面量和符號(hào)引用,也可以在運(yùn)行時(shí)動(dòng)態(tài)加入.
java堆:存放對(duì)象的實(shí)例,是垃圾回收的主戰(zhàn)場(chǎng),
創(chuàng)建一個(gè)對(duì)象比如執(zhí)行 new MyClass();
去常量池中尋找,查看類是否被加載.如果沒加載,則加載class.
在java堆中分配內(nèi)存空間,方式有以下兩種:
指針碰撞:把指針向空閑對(duì)象移動(dòng)與對(duì)象占用內(nèi)存大小相等的距離,使用的收集器有Serial、ParNes等
空閑列表:虛擬機(jī)維護(hù)一個(gè)列表,記錄可用的內(nèi)存塊,分配給對(duì)象列表中一塊足夠大的內(nèi)存空間,使用的收集器有CMS等.
如何分配內(nèi)存,由垃圾回收器決定.
內(nèi)存的具體分配過程中有同步和預(yù)留空白區(qū)的方式
內(nèi)存分配好后,再執(zhí)行init()方法,初始化實(shí)例.
對(duì)象頭對(duì)象頭主要記錄對(duì)象的hashcode,GC標(biāo)記,元數(shù)據(jù)地址,以及關(guān)于對(duì)象鎖的使用,年齡代,偏向線程等。
hash用于快速尋找對(duì)象
對(duì)象頭大小32bit/64bit,由虛擬機(jī)決定
實(shí)例數(shù)據(jù)區(qū)的數(shù)據(jù)類型,按照相似放在一起.
對(duì)象中的訪問定位句柄池
句柄池從堆中劃分
由實(shí)例地址和類型數(shù)據(jù)地址構(gòu)成
指針
可直接通過指針訪問到實(shí)例對(duì)象
句柄的使用,方便了實(shí)例位置的改變,可以不改變引用,但是訪問速度相對(duì)于指針低一些.
JVM垃圾回收 判斷可否回收的算法1.引用計(jì)數(shù)算法:
給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加1;當(dāng)引用失效時(shí),計(jì)數(shù)器值就減1;任何時(shí)刻計(jì)數(shù)器都為0的對(duì)象就是不再被使用的,垃圾收集器將回收該對(duì)象使用的內(nèi)存。
可達(dá)性分析算法:
通過一系列的名為GC Root的對(duì)象作為起點(diǎn),從這些節(jié)點(diǎn)向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個(gè)對(duì)象到GC Root沒有任何引用鏈相連時(shí),則該對(duì)象不可達(dá),該對(duì)象是不可使用的,垃圾收集器將回收其所占的內(nèi)存。
實(shí)例的位置:
java虛擬機(jī)棧(棧幀中的本地變量表)中的引用的對(duì)象。
方法區(qū)中的類靜態(tài)屬性引用的對(duì)象。
方法區(qū)中的常量引用的對(duì)象。
本地方法棧中JNI本地方法的引用對(duì)象。
不可達(dá)對(duì)象到死亡還需要兩次標(biāo)記,第一次,標(biāo)記后進(jìn)入F-Queue隊(duì)列,第二次標(biāo)記時(shí)只有finalize()中有拯救自己的方法的實(shí)例才能自救成功,比如將自己應(yīng)用給其它變量.
垃圾回收算法沒有被引用,即可被回收
所有實(shí)例都被回收
所有classLoader都被回收
java.lang.class對(duì)象沒有被任何地方引用,即無法在任何地方使用反射訪問類.
最終是否被回收,還得看JVM參數(shù)配置
java堆回收算法標(biāo)記清除算法: 先標(biāo)記判定,再一次性清除.
產(chǎn)生了大量碎片,且效率低下
復(fù)制算法: 把可用內(nèi)存劃分為兩塊,一塊用完后,就將活下來的實(shí)例放到另一塊內(nèi)存區(qū).
優(yōu)缺點(diǎn):沒有了碎片化問題,但內(nèi)存大小減少了一半
標(biāo)記整理算法: 在標(biāo)記-清除算法基礎(chǔ)上做了改進(jìn),標(biāo)記階段是相同的標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成之后不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),在移動(dòng)過程中清理掉可回收的對(duì)象,這個(gè)過程叫做整理。
標(biāo)記-整理算法相比標(biāo)記-清除算法的優(yōu)點(diǎn)是內(nèi)存被整理以后不會(huì)產(chǎn)生大量不連續(xù)內(nèi)存碎片問題。復(fù)制算法在對(duì)象存活率高的情況下就要執(zhí)行較多的復(fù)制操作,效率將會(huì)變低,而在對(duì)象存活率高的情況下使用標(biāo)記整理算法效率會(huì)大大提高。
分代收集算法: 根據(jù)內(nèi)存中對(duì)象的存活周期不同,將內(nèi)存劃分為幾塊,java的虛擬機(jī)中一般把內(nèi)存劃分為新生代和年老代,當(dāng)新創(chuàng)建對(duì)象時(shí)一般在新生代中分配內(nèi)存空間,當(dāng)新生代垃圾收集器回收幾次之后仍然存活的對(duì)象會(huì)被移動(dòng)到年老代內(nèi)存中,當(dāng)大對(duì)象在新生代中無法找到足夠的連續(xù)內(nèi)存時(shí)也直接在年老代中創(chuàng)建?,F(xiàn)在的Java虛擬機(jī)就聯(lián)合使用了分代復(fù)制、標(biāo)記-清除和標(biāo)記-整理算法.
java虛擬機(jī)垃圾收集器關(guān)注的內(nèi)存結(jié)構(gòu)如下:
研究表明,新生代中98%的對(duì)象是朝生夕死的短生命周期對(duì)象,所以不需要將新生代劃分為容量大小相等的兩部分內(nèi)存,而是將新生代分為Eden區(qū),Survivor from和Survivor to三部分,其占新生代內(nèi)存容量默認(rèn)比例分別為8:1:1,其中Survivor from和Survivor to總有一個(gè)區(qū)域是空白,只有Eden和其中一個(gè)Survivor總共90%的新生代容量用于為新創(chuàng)建的對(duì)象分配內(nèi)存,只有10%的Survivor內(nèi)存浪費(fèi),當(dāng)新生代內(nèi)存空間不足需要進(jìn)行垃圾回收時(shí),仍然存活的對(duì)象被復(fù)制到空白的Survivor內(nèi)存區(qū)域中,Eden和非空白的Survivor進(jìn)行標(biāo)記-清理回收,兩個(gè)Survivor區(qū)域是輪換的。
年老代中的對(duì)象一般都是長(zhǎng)生命周期對(duì)象,對(duì)象的存活率比較高,因此在年老代中使用標(biāo)記-整理垃圾回收算法。
Java虛擬機(jī)對(duì)年老代的垃圾回收稱為MajorGC/Full GC,次數(shù)相對(duì)比較少,每次回收的時(shí)間也比較長(zhǎng)。
堆分配和回收策略優(yōu)先在Eden上分配,空間不足,虛擬機(jī)發(fā)起minor GC.
大對(duì)象直接進(jìn)入老年代,防止折磨新生代空間.[參數(shù)設(shè)置 -XX:PretrnureSizeThreshold=[字節(jié)數(shù)]]
長(zhǎng)大后的對(duì)象進(jìn)入老年代,在survivor中熬過一次,就長(zhǎng)一歲,15歲時(shí)就進(jìn)入老年代[閾值設(shè)置 -XX:MaxTenuringThreshold=[歲數(shù)]]
相同年齡的對(duì)象,若大于或等于空間的一半,也直接進(jìn)入老年代.
oracle java虛擬機(jī)官方使用的HotSpot虛擬機(jī)
默認(rèn)老年代收集器MarkSweep,新生代收集器Scavenge
MarkSweep收集器:是以犧牲吞吐量為代價(jià)來獲得最短回收停頓時(shí)間的垃圾回收器。對(duì)于要求服務(wù)器響應(yīng)速度的應(yīng)用上,這種垃圾回收器非常適合。在啟動(dòng)JVM參數(shù)加上-XX:+UseConcMarkSweepGC ,這個(gè)參數(shù)表示對(duì)于老年代的回收采用CMS。CMS采用的基礎(chǔ)算法是:標(biāo)記—清除。
流程:
初始標(biāo)記 :在這個(gè)階段,需要虛擬機(jī)停頓正在執(zhí)行的任務(wù),官方的叫法STW(Stop The Word)。這個(gè)過程從垃圾回收的"根對(duì)象"開始,只掃描到能夠和"根對(duì)象"直接關(guān)聯(lián)的對(duì)象,并作標(biāo)記。所以這個(gè)過程雖然暫停了整個(gè)JVM,但是很快就完成了。
并發(fā)標(biāo)記 :這個(gè)階段緊隨初始標(biāo)記階段,在初始標(biāo)記的基礎(chǔ)上繼續(xù)向下追溯標(biāo)記。并發(fā)標(biāo)記階段,應(yīng)用程序的線程和并發(fā)標(biāo)記的線程并發(fā)執(zhí)行,所以用戶不會(huì)感受到停頓。
并發(fā)預(yù)清理 :并發(fā)預(yù)清理階段仍然是并發(fā)的。在這個(gè)階段,虛擬機(jī)查找在執(zhí)行并發(fā)標(biāo)記階段新進(jìn)入老年代的對(duì)象(可能會(huì)有一些對(duì)象從新生代晉升到老年代, 或者有一些對(duì)象被分配到老年代)。通過重新掃描,減少下一個(gè)階段"重新標(biāo)記"的工作,因?yàn)橄乱粋€(gè)階段會(huì)Stop The World。
重新標(biāo)記 :這個(gè)階段會(huì)暫停虛擬機(jī),收集器線程掃描在CMS堆中剩余的對(duì)象。掃描從"跟對(duì)象"開始向下追溯,并處理對(duì)象關(guān)聯(lián)。
并發(fā)清理 :清理垃圾對(duì)象,這個(gè)階段收集器線程和應(yīng)用程序線程并發(fā)執(zhí)行。
并發(fā)重置 :這個(gè)階段,重置CMS收集器的數(shù)據(jù)結(jié)構(gòu),等待下一次垃圾回收。
新生代收集器,復(fù)制算法,并行收集,面向吞吐量要求(吞吐量?jī)?yōu)先收集器)。
吞吐量=用戶代碼運(yùn)行時(shí)間/(用戶代碼運(yùn)行時(shí)間+垃圾回收時(shí)間)
-XX:MaxGCPauseMillis:控制最大垃圾收集停頓時(shí)間,大于零的毫秒數(shù)。
-XX:GCTimeRatio:吞吐量大小,0到100的整數(shù),垃圾收集時(shí)間占總時(shí)間的比例,計(jì)算1/(1+n)gc時(shí)間占用比例。
-XX:UseAdaptiveSizePolicy:打開之后,就不需要設(shè)置新生代大?。?Xmn),Edian,survivor比例及(-XX:SurvivorRatio)晉升老年代年齡(-XX:PretenureSizeThreshold),虛擬機(jī)根據(jù)系統(tǒng)運(yùn)行狀況,調(diào)整停頓時(shí)間,吞吐量, GC自適應(yīng)調(diào)節(jié)策略,區(qū)別parnew。
永久代:-XX:PermSize20M -XX:MaxPermSize20M
堆大小: -Xms20M -Xmx20M
新生代: -Xmn10M
Eden與survior比率: -XX:SurvivorRation=8
讓大對(duì)象直接進(jìn)入老年代: -XX:PretrnureSizeThreshold=1B
年齡閾值:-XX:MaxTenuringThreshold=15
日志命令:參考連接
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/73706.html
摘要:之前的堆內(nèi)存示意圖從上圖可以看出堆內(nèi)存的分為新生代老年代和永久代。對(duì)象優(yōu)先在區(qū)分配目前主流的垃圾收集器都會(huì)采用分代回收算法,因此需要將堆內(nèi)存分為新生代和老年代,這樣我們就可以根據(jù)各個(gè)年代的特點(diǎn)選擇合適的垃圾收集算法。 上文回顧:《可能是把Java內(nèi)存區(qū)域講的最清楚的一篇文章》 寫在前面 本節(jié)常見面試題: 問題答案在文中都有提到 如何判斷對(duì)象是否死亡(兩種方法)。 簡(jiǎn)單的介紹一下強(qiáng)引用...
摘要:深入理解虛擬機(jī)高級(jí)特性與最佳實(shí)踐第二版讀書筆記與常見面試題總結(jié)上篇文章傳送門深入理解虛擬機(jī)之內(nèi)存區(qū)域本節(jié)常見面試題推薦帶著問題閱讀,問題答案在文中都有提到如何判斷對(duì)象是否死亡兩種方法。虛引用主要用來跟蹤對(duì)象被垃圾回收的活動(dòng)。 《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(第二版》讀書筆記與常見面試題總結(jié) 上篇文章傳送門: 深入理解虛擬機(jī)之Java內(nèi)存區(qū)域 本節(jié)常見面試題(推薦帶著...
摘要:它一般運(yùn)行在模式下的虛擬機(jī)。設(shè)置最大垃圾收集停頓時(shí)間設(shè)置吞吐量大小開關(guān)參數(shù),打開以后就由虛擬機(jī)自動(dòng)調(diào)節(jié)策略。 Java內(nèi)存區(qū)域 showImg(https://user-gold-cdn.xitu.io/2019/5/21/16adb14e60767c12); 程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行字節(jié)碼的行號(hào)指示器,字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令 虛擬機(jī)...
摘要:引言垃圾收集技術(shù)并不是語(yǔ)言首創(chuàng)的,年誕生于的是第一門真正使用內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)的語(yǔ)言。垃圾收集器所關(guān)注的就是這部分內(nèi)存。收集器是收集器的多線程版,它是第一款并發(fā)收集器。經(jīng)常出現(xiàn)大對(duì)象會(huì)導(dǎo)致多次出發(fā)垃圾收集。 引言 垃圾收集技術(shù)并不是Java語(yǔ)言首創(chuàng)的,1960年誕生于MIT的Lisp是第一門真正使用內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)的語(yǔ)言。垃圾收集技術(shù)需要考慮的三個(gè)問題是: 哪些內(nèi)存需...
閱讀 2168·2021-11-02 14:48
閱讀 2822·2019-08-30 14:19
閱讀 2999·2019-08-30 13:19
閱讀 1359·2019-08-29 16:17
閱讀 3339·2019-08-26 14:05
閱讀 3050·2019-08-26 13:58
閱讀 3133·2019-08-23 18:10
閱讀 1170·2019-08-23 18:04