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