日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

jvm gc垃圾回收机制和参数说明amp;amp;Java JVM 垃圾回收(GC 在什么时候,对什么东西,做了什么事情)

發(fā)布時(shí)間:2025/3/12 java 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jvm gc垃圾回收机制和参数说明amp;amp;Java JVM 垃圾回收(GC 在什么时候,对什么东西,做了什么事情) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

jvm?gc(垃圾回收機(jī)制)

Java JVM? 垃圾回收(GC 在什么時(shí)候,對(duì)什么東西,做了什么事情)

  • 前言:(先大概了解一下整個(gè)過程)
  • 作者:知乎用戶
    鏈接:https://www.zhihu.com/question/27339390/answer/36511809
    來源:知乎
    著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
    ?

    java堆(JavaHeap)
    1.用來存放對(duì)象的,幾乎所有對(duì)象都放在這里,被線程共享的,或者說是被棧共享的
    2.堆又可以分為新生代和老年代,實(shí)際還有一個(gè)區(qū)域叫永久代,但是jdk1.7已經(jīng)去永久代了,所以可以當(dāng)作沒有,永久代是當(dāng)jvm啟動(dòng)時(shí)就存放的JDK自身的類和接口數(shù)據(jù),關(guān)閉則釋放。
    新生代可以分為Eden區(qū)和兩個(gè)幸存區(qū),這么設(shè)計(jì)是為了更好地利用內(nèi)存 之前的設(shè)計(jì)是只分為兩部分一樣一半 后來發(fā)現(xiàn)這樣只利用到了一半的內(nèi)存 才改為按比例分成三個(gè)區(qū)的,使用的是復(fù)制回收算法,兩個(gè)幸存區(qū)是較小的區(qū)域。邏輯是每次使用Eden區(qū)和其中一個(gè)幸存區(qū),回收時(shí)將其還存活著的對(duì)象一次性的復(fù)制到另一個(gè)幸存區(qū)中,最后清理到剛才使用的Eden和其中一個(gè)幸存區(qū)。
    美團(tuán)的面試官也問了這個(gè)問題,他也說了他的理解,我感覺可能是不準(zhǔn)確的。
    新建對(duì)象就在Eden區(qū),Eden就是伊甸,顧名思義。但是并不是對(duì)象最活躍的區(qū)域,對(duì)象最活躍的區(qū)域是老年代,因?yàn)榻?jīng)過各種垃圾回收之后對(duì)象都跑到這里來了。
    3.內(nèi)存溢出
    內(nèi)存溢出其實(shí)沒什么好講的,滿了就會(huì)溢出。怎么才能滿呢,不斷創(chuàng)建對(duì)象,那問題又來了,創(chuàng)建多了被回收怎么辦,好辦,將新建的對(duì)象存到list里去,就不會(huì)回收了,為什么呢,因?yàn)閖vm判定一個(gè)對(duì)象的死活就是根據(jù)對(duì)象是不是被引用。
    此外堆跟隨jvm的,有jvm就有堆。堆也是垃圾回收的主要區(qū)域,又叫GC堆,垃圾堆,玩笑。
    jvm棧
    1.要說棧是用來存什么的,其實(shí)我感覺不嚴(yán)謹(jǐn),棧是運(yùn)行時(shí)創(chuàng)建的,是跟隨線程的,它不是用來存什么的,那它用來干什么的,它是用來存棧幀的,沒有圖不太好說呢,等下我去截個(gè)圖。

    <img src="https://pic1.zhimg.com/50/f7541e5d33d1d8b412dd0556c7e4b10d_hd.jpg" data-rawwidth="481" data-rawheight="713" class="origin_image zh-lightbox-thumb" width="481" data-original="https://pic1.zhimg.com/f7541e5d33d1d8b412dd0556c7e4b10d_r.jpg">圖來了我就不用多說了。每個(gè)棧幀其實(shí)可以理解為一個(gè)方法,我是這么理解的,之間的關(guān)系就是調(diào)用。
    2.棧的好處就是不需要垃圾回收,隨著線程結(jié)束內(nèi)存就釋放。
    3.但是并不是說就不會(huì)內(nèi)存溢出,那么棧的內(nèi)存溢出是怎么產(chǎn)生的呢,肯定也是滿了,這個(gè)滿了怎么理解呢,一是要申請(qǐng)的不夠了,二是jvm內(nèi)存太小,這是個(gè)有趣的問題。但是產(chǎn)生的錯(cuò)誤卻是不一樣的,如果創(chuàng)建一個(gè)void方法調(diào)用自身,錯(cuò)誤是stackoverflowError,如果不斷創(chuàng)建線程則會(huì)outOfMemoryError。這里就有一個(gè)比較高級(jí)的問題了,對(duì)于第二種多線程內(nèi)存溢出該怎么解決呢,深入理解jvm一書中給出的解決方案是這樣的,通過減小最大堆和棧容量來換取更多的線程。
    方法區(qū)和運(yùn)行時(shí)常量池
    1.方法區(qū)是堆的一個(gè)邏輯區(qū)域,但是又叫非堆。運(yùn)行時(shí)常量池又是方法區(qū)的一部分,真正的一部分。方法區(qū)并不是存方法的,存方法的應(yīng)該是棧或者棧幀。方法區(qū)存的是類信息、常量、靜態(tài)變量等,也是被線程共享的區(qū)域。運(yùn)行時(shí)常量池存放的是編譯期生產(chǎn)的各種字面量和符號(hào)引用。
    2.這塊內(nèi)存區(qū)域的回收沒啥好說的,因?yàn)槲乙膊惶宄?#xff0c;我只知道HotSpot的設(shè)計(jì)團(tuán)隊(duì)選擇把GC分代擴(kuò)展至方法區(qū)了,或者是使用永久代實(shí)現(xiàn)方法區(qū)。
    3.內(nèi)存是肯定會(huì)溢出的,不斷創(chuàng)建類會(huì)導(dǎo)致方法區(qū)內(nèi)存溢出,而不斷將常量放入常量池(String.intern()),常量池也會(huì)內(nèi)存溢出。
  • ?

  • 這里主要講分代回收機(jī)制
  • 內(nèi)存結(jié)構(gòu)
    • 年輕代:一個(gè) Eden 區(qū)和兩個(gè) Survivor 區(qū)
    • 年老代:一個(gè)?old 區(qū)
    • 持久代:一個(gè) Permanent 區(qū)
  • 新建立的對(duì)象先放到 Eden 區(qū)中,如果 Eden 區(qū)滿了之后,就會(huì)執(zhí)行標(biāo)記-清除算法回收 Eden 區(qū)垃圾,并把生存的對(duì)象放到 Survivor 的其中一個(gè)區(qū)中,兩個(gè) Survivor 區(qū)有一個(gè)必須是空的,當(dāng)其中一個(gè) Survivor 滿了之后,采用標(biāo)記-復(fù)制方法,把生存的對(duì)象放到另外一個(gè) Survivor 區(qū)。

  • 在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對(duì)象,就會(huì)被放到年老代中。

  • 持久代用于存放靜態(tài)文件、Java類、方法、靜態(tài)對(duì)象等。

    • 觸發(fā) gc 的條件
  • minor GC: 當(dāng)新對(duì)象生成,并且在Eden申請(qǐng)空間失敗時(shí),就會(huì)觸發(fā)minor GC,對(duì)Eden區(qū)域進(jìn)行GC,清除非存活對(duì)象,并且把尚且存活的對(duì)象移動(dòng)到Survivor區(qū)。然后整理Survivor的兩個(gè)區(qū)

  • Full GC: 對(duì)整個(gè)堆進(jìn)行整理,包括Young、old 和Perm。Full GC因?yàn)樾枰獙?duì)整個(gè)對(duì)進(jìn)行回收,以下原因可能導(dǎo)致 Full GC

    • 年老代(old )被寫滿
    • 持久代(Perm)被寫滿
    • System.gc()被顯示調(diào)用
    • 上一次GC之后Heap的各域分配策略動(dòng)態(tài)變化

    ?

    jvm參數(shù)說明~~

    ?

    ?

    1.jvm的結(jié)構(gòu):

    ?

    • 方法區(qū): 也是 jvm gc 中的持久代,它用于存儲(chǔ)虛擬機(jī)加載的類信息、常量、靜態(tài)變量、是各個(gè)線程共享的內(nèi)存區(qū)域。

    • 堆: 也是被各個(gè)線程共享的內(nèi)存區(qū)域,在JVM啟動(dòng)時(shí)創(chuàng)建。該內(nèi)存區(qū)域存放了對(duì)象實(shí)例及數(shù)組,包括 jvm gc 中的年輕代和年老代。

    • 虛擬機(jī)棧: 每個(gè)方法被執(zhí)行的時(shí)候 都會(huì)創(chuàng)建一個(gè)“棧幀”用于存儲(chǔ)局部變量表(包括參數(shù))、操作棧、方法出口等信息。每個(gè)方法被調(diào)用到執(zhí)行完的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。聲明周期與線程相同,是線程私有的。而局部變量表中繼承 Object 的對(duì)象都是引用堆和方法區(qū)的內(nèi)存,而基本數(shù)據(jù)類型的對(duì)象(boolean、byte、char、short、int、float、long、double)則是直接保存存在棧中。

    • 本地方法棧: 與虛擬機(jī)棧基本類似,區(qū)別在于虛擬機(jī)棧為虛擬機(jī)執(zhí)行的java方法服務(wù),而本地方法棧則是為Native方法服務(wù)。

    • 程序計(jì)數(shù)器: 類似于PC寄存器,是一塊較小的內(nèi)存區(qū)域,通過程序計(jì)數(shù)器中的值尋找要執(zhí)行的指令的字節(jié)碼,由于多線程間切換時(shí)要恢復(fù)每一個(gè)線程的當(dāng)前執(zhí)行位置,所以每個(gè)線程都有自己的程序計(jì)算器。

    ?

  • 各參數(shù)說明
    • -Xmx: 堆內(nèi)存大小的上限

    • -Xms: 堆內(nèi)存大小的初始值

    • -Xmn: 年輕代內(nèi)存大小,年輕代包括兩個(gè)區(qū),Eden 和 Survivor 區(qū),Suvrvior 區(qū)還被平均分成了兩塊 from space 和 to space

    • -Xss: 每條線程內(nèi)存大小

    • -XX:PermSize(java8 之后變成了 -XX:MetaspaceSize): 持久代初始內(nèi)存大小

    • -XX:MaxPermSize(java8 之后變成了 -XX:MaxMetaspaceSize): 最大持久代內(nèi)存大小

    • -XX:NewSize: 新生代初始堆內(nèi)存占用的默認(rèn)值

    • -XX:MaxNewSize: 新生代占整個(gè)堆內(nèi)存的最大值

    • -XX:NewRatio: 老年代對(duì)比新生代的空間大小, 比如2代表老年代空間是新生代的兩倍大小

    • -XX:SurvivorRatio: Eden/Survivor的值,比如8表示Survivor:Eden=1:8, 因?yàn)閟urvivor區(qū)有2個(gè), 所以Eden的占比為8/10

    • -XX:CompressedClassSpaceSize: 類指針壓縮空間大小

  • 指針壓縮介紹
    • 64位平臺(tái)上默認(rèn)打開
    • 使用-XX:+UseCompressedOops壓縮對(duì)象指針?
      "oops"指的是普通對(duì)象指針("ordinary" object pointers)?
      Java堆中對(duì)象指針會(huì)被壓縮成32位?
      使用堆基地址(如果堆在低26G內(nèi)存中的話,基地址為0)
    • 使用-XX:+UseCompressedClassPointers選項(xiàng)來壓縮類指針?
      對(duì)象中指向類元數(shù)據(jù)的指針會(huì)被壓縮成32位?
      類指針壓縮空間會(huì)有一個(gè)基地址

    ?

    ?

  • 元空間和類指針壓縮空間的區(qū)別
    • 類指針壓縮空間只包含類的元數(shù)據(jù),比如InstanceKlass, ArrayKlass?
      僅當(dāng)打開了UseCompressedClassPointers選項(xiàng)才生效?
      為了提高性能,Java中的虛方法表也存放到這里

    • 元空間包含類的其它比較大的元數(shù)據(jù),比如方法,字節(jié)碼,常量池等

    ?

    ?

    ?

    Java JVM? 垃圾回收(GC 在什么時(shí)候對(duì)什么東西,做了什么事情

    ?

    ?

    在什么時(shí)候

    首先需要知道,GC又分為minor GC 和 Full Gc(也稱為Major GC)。Java 堆內(nèi)存分為新生代和老年代(持久代在方法區(qū)上),新生代中又分為1個(gè)Eden區(qū)域 和兩個(gè) Survivor區(qū)域

    那么對(duì)于 Minor GC 的觸發(fā)條件:大多數(shù)情況下,直接在 Eden 區(qū)中進(jìn)行分配。如果 Eden區(qū)域沒有足夠的空間,那么就會(huì)發(fā)起一次 Minor GC

    ?

    對(duì)于 Full GC(Major GC)的觸發(fā)條件:也是如果老年代沒有足夠空間的話,那么就會(huì)進(jìn)行一次 Full GC。

    Ps:上面所說的只是一般情況下,實(shí)際上,需要考慮一個(gè)空間分配擔(dān)保的問題:

    在發(fā)生Minor GC之前,虛擬機(jī)會(huì)先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對(duì)象的總空間。如果大于則進(jìn)行Minor? GC,如果小于則看HandlePromotionFailure設(shè)置是否允許擔(dān)保失敗(不允許則直接Full GC)。如果允許,那么會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對(duì)象的平均大小,如果大于則嘗試Minor GC(如果嘗試失敗也會(huì)觸發(fā)Full GC),如果小于則進(jìn)行Full GC。

    空間分配擔(dān)保總結(jié):

    minor?GC :1.?老年代最大連續(xù)可用的空間大于新生代所有對(duì)象的總空間; 2.?老年代最大連續(xù)可用空間小于新生代所有對(duì)象的總空間,并且HandlePromotionFailure設(shè)置允許擔(dān)保失敗,且老年代最大可用連續(xù)空間大于歷次晉升老年代對(duì)象的平均大小。

    full?GC :1.?老年代最大連續(xù)可用的空間小于新生代所有對(duì)象的總空間,并且HandlePromotionFailure設(shè)置不允許擔(dān)保失敗;

    2。?如果上面的2嘗試minor?GC失敗,則出發(fā)full?GC。

    ?

    ?

    但是,具體到什么時(shí)刻執(zhí)行,這個(gè)是由系統(tǒng)來進(jìn)行決定,是無法預(yù)測(cè)的。

    對(duì)什么東西(總結(jié):從GC?root開始搜索,搜索不到的對(duì)象,并且經(jīng)過第一個(gè)標(biāo)記,清理之后,仍然沒有復(fù)活的對(duì)象)

    ?

    主要根據(jù)可達(dá)性分析算法,如果一個(gè)對(duì)象不可達(dá),那么就是可以回收的;如果一個(gè)對(duì)象可達(dá),那么這個(gè)對(duì)象就不可以回收。對(duì)于可達(dá)性分析算法,它是通過一系列稱為“GC Roots” 的對(duì)象作為起始點(diǎn),當(dāng)一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相接的時(shí)候,那么這個(gè)對(duì)象就是不可達(dá),就可以被回收。如下圖:

    ?

    這個(gè)GC Root 對(duì)象可以是一些靜態(tài)的對(duì)象,Java方法的local變量或參數(shù), native 方法引用的對(duì)象,活著的線程。

    做了什么事情

    主要做了清理對(duì)象,整理內(nèi)存的工作。Java堆分為新生代和老年代,采用了不同的回收方式。例如新生代采用了標(biāo)記復(fù)制算法,老年代采用了標(biāo)記整理法。在新生代中,分為一個(gè)Eden 區(qū)域和兩個(gè)Survivor區(qū)域,真正使用的是一個(gè)Eden區(qū)域和一個(gè)Survivor區(qū)域,GC的時(shí)候,會(huì)把存活的對(duì)象放入到另外一個(gè)Survivor區(qū)域中,然后再把這個(gè)Eden區(qū)域和Survivor區(qū)域清除。那么對(duì)于老年代,采用的是標(biāo)記整理法,首先標(biāo)記出存活的對(duì)象,然后再移動(dòng)到一端。這樣也有利于減少內(nèi)存碎片。

    ?

    ?

    ?

    ?

    額外補(bǔ)充:

    ?

    ?

  • SafePoint是什么
  • 比如GC的時(shí)候必須要等到Java線程都進(jìn)入到safepoint的時(shí)候VMThread才能開始執(zhí)行GC,

  • 循環(huán)的末尾 (防止大循環(huán)的時(shí)候一直不進(jìn)入safepoint,而其他線程在等待它進(jìn)入safepoint)
  • 方法返回前
  • 調(diào)用方法的call之后
  • 拋出異常的位置
  • GC收集器有哪些?CMS收集器與G1收集器的特點(diǎn)。
  • 行收集器:串行收集器使用一個(gè)單獨(dú)的線程進(jìn)行收集,GC時(shí)服務(wù)有停頓時(shí)間

    并行收集器:次要回收中使用多線程來執(zhí)行

    CMS收集器是基于“標(biāo)記—清除”算法實(shí)現(xiàn)的,經(jīng)過多次標(biāo)記才會(huì)被清除

    G1從整體來看是基于“標(biāo)記—整理”算法實(shí)現(xiàn)的收集器,從局部(兩個(gè)Region之間)上來看是基于“復(fù)制”算法實(shí)現(xiàn)的

    [GC收集器]: http://www.jianshu.com/p/50d5c88b272d

  • 類加載的幾個(gè)過程:
  • 加載、驗(yàn)證、準(zhǔn)備、解析、初始化。然后是使用和卸載了

    通過全限定名來加載生成class對(duì)象到內(nèi)存中,然后進(jìn)行驗(yàn)證這個(gè)class文件,包括文件格式校驗(yàn)、元數(shù)據(jù)驗(yàn)證,字節(jié)碼校驗(yàn)等。準(zhǔn)備是對(duì)這個(gè)對(duì)象分配內(nèi)存。解析是將符號(hào)引用轉(zhuǎn)化為直接引用(指針引用),初始化就是開始執(zhí)行構(gòu)造器的代碼

  • 雙親委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。
  • Bootstrap ClassLoader:啟動(dòng)類加載器,負(fù)責(zé)將$ Java_Home/lib下面的類庫加載到內(nèi)存中(比如rt.jar

    Extension ClassLoader:標(biāo)準(zhǔn)擴(kuò)展(Extension)類加載器,它負(fù)責(zé)將$Java_Home /lib/ext或者由系統(tǒng)變量 java.ext.dir指定位置中的類庫加載到內(nèi)存中。

    ApplicationClassLoader:它負(fù)責(zé)將系統(tǒng)類路徑(CLASSPATH)中指定的類庫加載到內(nèi)存中。開發(fā)者可以直接使用系統(tǒng)類加載器

    雙親委派模型是某個(gè)特定的類加載器在接到加載類的請(qǐng)求時(shí),首先將加載任務(wù)委托給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務(wù),就成功返回;只有父類加載器無法完成此加載任務(wù)時(shí),才自己去加載。-----例如類java.lang.Object,它存在在rt.jar中,無論哪一個(gè)類加載器要加載這個(gè)類,最終都是委派給處于模型最頂端的Bootstrap ClassLoader進(jìn)行加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個(gè)類。相反,如果沒有雙親委派模型而是由各個(gè)類加載器自行加載的話,如果用戶編寫了一個(gè)java.lang.Object的同名類并放在ClassPath中,那系統(tǒng)中將會(huì)出現(xiàn)多個(gè)不同的Object類,程序?qū)⒒靵y

  • 分派
  • 靜態(tài)分派(重載)與動(dòng)態(tài)分派(重寫)。

  • 你知道哪些JVM性能調(diào)優(yōu)
  • 設(shè)定堆最小內(nèi)存大小-Xms

  • -Xmx:堆內(nèi)存最大限制。
  • 設(shè)定新生代大小。
    新生代不宜太小,否則會(huì)有大量對(duì)象涌入老年代
  • -XX:NewSize:新生代大小

    -XX:NewRatio 新生代和老生代占比

    -XX:SurvivorRatio:伊甸園空間和幸存者空間的占比

  • 設(shè)定垃圾回收器
  • ? 年輕代用 -XX:+UseParNewGC (串行) 年老代用-XX:+UseConcMarkSweepGC (CMS)

  • 設(shè)定鎖的使用
  • 多線程下關(guān)閉偏向鎖,比較浪費(fèi)資源

  • g1 和 cms 區(qū)別,吞吐量?jī)?yōu)先和響應(yīng)優(yōu)先的垃圾收集器選擇
  • 1CMS是一種以最短停頓時(shí)間為目標(biāo)的收集器

    響應(yīng)優(yōu)先選擇CMS,吞吐量高選擇G1

  • 當(dāng)出現(xiàn)了內(nèi)存溢出,你怎么排錯(cuò)
  • 用jmap看內(nèi)存情況,然后用 jstack主要用來查看某個(gè)Java進(jìn)程內(nèi)的線程堆棧信息

    ?

    參考:?http://icyfenix.iteye.com/blog/715301

    ?

    https://github.com/konginyan/Learning-Notes/blob/master/java/jvm%20%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E.md

    ?

    總結(jié)

    以上是生活随笔為你收集整理的jvm gc垃圾回收机制和参数说明amp;amp;Java JVM 垃圾回收(GC 在什么时候,对什么东西,做了什么事情)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。