jvm 方面

  根據(jù)VM規(guī)范,VM應(yīng)該被劃分為五塊區(qū)域——即VM棧、堆、方法區(qū)、程序計數(shù)器、本地方法棧五個部分。如下圖所示(這里介紹的是JDK1.8 JVM運行時內(nèi)存數(shù)據(jù)區(qū)域劃分。1.8同1.7比,最大的差別就是:元數(shù)據(jù)區(qū)取代了永久代。元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元數(shù)據(jù)空間并不在虛擬機中,而是使用本地內(nèi)存,具體實際例子:jdk1.4引入了NIO,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,或者使用nio.DirectByteBuffer ):---引入堆內(nèi)存和堆外內(nèi)存的使用,常規(guī)來講GC的回收是針對堆內(nèi)存的回收,通過jvm參數(shù)-Xms,-Xmx等設(shè)置堆的大小和最大值。
1590737548.png
  其中:棧:函數(shù)中定義的基本類型變量和引用變量都在棧內(nèi)存,堆內(nèi)存: new創(chuàng)建的對象,數(shù)組,以及對象的實例變量。

  Jdk1.8的堆的分配情況:Eden,Old, surviver ,默認分配規(guī)則:

    老年代 : 三分之二的堆空間

    年輕代 : 三分之一的堆空間
    *   eden區(qū): 8/10 的年輕代空間
    *   survivor0 : 1/10 的年輕代空間
    *   survivor1 : 1/10 的年輕代空間

  命令行上執(zhí)行如下命令,查看所有默認的jvm參數(shù): java -XX:+PrintFlagsFinal -version

  通過命令: 默認2:8
image.png
  Eden:該區(qū)域是最主要的剛創(chuàng)建的對象的內(nèi)存分配區(qū)域,絕大多數(shù)對象都會被創(chuàng)建到這里(除了部分大對象通過內(nèi)存擔保機制創(chuàng)建到Old區(qū)域,默認大對象都是能夠存活較長時間的),該區(qū)域的對象大部分都是短時間都會死亡的,故垃圾回收器針對該部分主要采用標記整理算法了回收該區(qū)域。

  Surviver:該區(qū)域也是屬于新生代的區(qū)域,該區(qū)域是將在Eden中未被清理的對象存放到該區(qū)域中,該區(qū)域分為兩塊區(qū)域,采用的是復制算法,每次只使用一塊,Eden與Surviver區(qū)域的比例是8:1,是根據(jù)大量的業(yè)務(wù)運行總結(jié)出來的規(guī)律。

  Old:該區(qū)域是屬于老年代,一般能夠在Surviver中沒有被清除出去的對象才會進入到這塊區(qū)域,該區(qū)域主要是采用標記清除算法。

  java GC的字節(jié)碼解碼編譯器:

   為了滿足不同用戶場景的需要,HotSpot 內(nèi)置了多個即時編譯器:C1、C2 和 Graal。Graal 是 Java 10 正式引入的實驗性即時編譯器,在專欄的第四部分我會詳細介紹,這里暫不做討論。之所以引入多個即時編譯器,是為了在編譯時間和生成代碼的執(zhí)行效率之間進行取舍。C1 又叫做 Client 編譯器,面向的是對啟動性能有要求的客戶端 GUI 程序,采用的優(yōu)化手段相對簡單,因此編譯時間較短。

  C2 又叫做 Server 編譯器,面向的是對峰值性能有要求的服務(wù)器端程序,采用的優(yōu)化手段相對復雜,因此編譯時間較長,但同時生成代碼的執(zhí)行效率較高。

  從 Java 7 開始,HotSpot 默認采用分層編譯的方式:熱點方法首先會被 C1 編譯,而后熱點方法中的熱點會進一步被 C2 編譯。

  為了不干擾應(yīng)用的正常運行,HotSpot 的即時編譯是放在額外的編譯線程中進行的。HotSpot 會根據(jù) CPU 的數(shù)量設(shè)置編譯線程的數(shù)目,并且按 1:2 的比例配置給 C1 及 C2 編譯器。

  在計算資源充足的情況下,字節(jié)碼的解釋執(zhí)行和即時編譯可同時進行。編譯完成后的機器碼會在下次調(diào)用該方法時啟用,以替換原本的解釋執(zhí)行。

java jvm的GC回收算法:

1、標記清除

  使用場景

  存活對象較多的情況下比較高效

  適用于年老代(即舊生代)

缺點:

   容易產(chǎn)生內(nèi)存碎片,再來一個比較大的對象時(典型情況:該對象的大小大于空閑表中的每一塊兒大小但是小于其中兩塊兒的和),會提前觸發(fā)垃圾回收

   掃描了整個空間兩次(第一次:標記存活對象;第二次:清除沒有標記的對象)

  2、復制算法(現(xiàn)在的商業(yè)虛擬機都采用這種收集算法來回收新生代)

  使用場景

  存活對象較少的情況下比較高效,

  掃描了整個空間一次(標記存活對象并復制移動)

使用年輕代

缺點:

  需要一塊空的內(nèi)存空間

  需要復制移動對象

  前提:復制算法的高效性是建立在存活對象少、垃圾對象多的前提下的

  3、標記整理

  標記-壓縮算法是一種老年代的回收算法,它在標記-清除算法的基礎(chǔ)上做了一些優(yōu)化。

    首先也需要從根節(jié)點開始對所有可達對象做一次標記,但之后,它并不簡單地清理未標記的對象,而是將所有的存活對象壓縮到內(nèi)存的一端。之后,清理邊界外所有的空間。這種方法既避免了碎片的產(chǎn)生,又不需要兩塊相同的內(nèi)存空間,因此,其性價比比較高。

  4、分代收集算法 :CMS 和G1

  jdk1.8的 G1垃圾回收

  在G1中,堆內(nèi)存通常被分為幾千個大小相同region。同樣的,在ZGC中堆內(nèi)存也被分成大量的區(qū)域,它們被稱為page,不同的是,ZGC中page的大小是不同的。

  ZGC有3種不同的頁面類型:小型(2MB大?。行停?2MB大?。┖痛笮停?MB的倍數(shù))。

  在小頁面中分配小對象(最大256KB大小),在中間頁面中分配中型對象(最多4MB)。大頁面中分配大于4MB的對象。大頁面只能存儲一個對象,與小頁面或中間頁面相對應(yīng)。

  有些令人困惑的大頁面實際上可能小于中等頁面(例如,對于大小為6MB的大對象)。

總結(jié)起來,G1的年輕代收集歸納如下:

*   堆就是一整塊內(nèi)存空間,被分為多個heap區(qū)(regions)。
*   年輕代內(nèi)存由一組不連續(xù)的heap塊也就是region組成. 這使得在需要時很容易進行容量調(diào)整。
*   年輕代的垃圾收集,或者叫 young GCs, 會發(fā)生stop the world。 在回收時所有的應(yīng)用程序線程都會被暫停。
*   年輕代 GC 通過多線程并行進行。
*   存活的對象被拷貝到新的 survivor 塊或者老年代。

老生代的G1垃圾回收有以下幾個關(guān)鍵點:

  1、并發(fā)標記階段(Concurrent Marking Phase)

*   活躍度信息在程序運行的時候就被并行的計算了出來。
*   活躍度(liveness)信息標記出哪些區(qū)域塊最適合回收,在轉(zhuǎn)移暫停期間最適合回收掉。
*   沒有sweep階段。但CMS是有這個階段的。

2、重新標記階段(Remark Phase)

*   使用了Snapshot-at-the-Beginning (SATB)算法,這個要比CMS的算法快很多。
*   完全空的區(qū)域塊會被直接回收掉。

3、復制/清除階段(Copying/Cleanup Phase)

*   年輕代和老年代會被同時回收。
*   老年代的區(qū)域塊會不會被選擇,取決于它的活躍度。

  垃圾回收有兩種類型:Minor GC(對新生代進行回收,不會影響到年老代。因為新生代的 Java 對象大多死亡頻繁,所以 Minor GC 非常頻繁,一般在這里使用速度快、效率高的算法,使垃圾回收能盡快完成。) 和 Full GC (也叫

  Major GC,對整個堆進行回收,包括新生代和老年代。由于Full GC需要對整個堆進行回收,所以比MinorGC要慢,因此應(yīng)該盡可能減少Full GC的次數(shù),導致FullGC的原因包括:老年代被寫滿、永久代(Perm)被寫滿和System.gc()被顯式調(diào)用等)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容