JVM學習筆記(一)

一、jvm內存模型總體分類

     1, 程序計數器(program count register ):

           多線程時,當線程數超過CPU數量或CPU內核數量,線程之間就要根據時間片搶奪CPU時間資源。因此每個線程要有一個獨立的程序計數器,記錄下一條要運行的指令。

           線程私有的內存區域。

           如果執行的是JAVA方法,計數器記錄正在執行的java字節碼地址;如果執行的是native方法,則計數器爲空。

     2,虛擬機棧(VM Stack)

          線程私有的,與線程在同一時間創建。

         管理java方法執行的內存模型。

         每個方法執行時都會創建一個幀棧來存儲方法的變量表、操作數棧,動態鏈接方法,返回值,返回地址等信息。

        如果請求的棧深度大於最大可大深度,則拋出StackOverflowError;如果棧是可動態擴展的,但沒有內存空間支持擴展,則拋出OutofMemoryError。

    3,本地方法區(native method area )

       線程私有的

        和虛擬機功能相似,但管理的不是java方法,是本地方法,本地方法是用C實現的。

    4,java堆(java heap)

        線程共享的,存放所有對象的實例和數組。

        垃圾回收的主要區域。可以分爲新生代和老生代(tenured)

        新生代用於存放剛創建的對象以及年輕的對象,如果對象一直沒有被回收,生存得足夠長,老年對象就會被移入老年代。

        新生代又可進一步細分爲eden、survivorSpace0(s0,from space)、survivorSpace1(s1,to space)。

        剛創建的對象都放入eden,s0和s1都至少經過一次GC並倖存。如果倖存對象經過一定時間仍存在,則進入老年代(tenured)。  

    5,方法區(method area )

   線程共享的,用於存放被虛擬機加載的類的元數據信息:如常量、靜態變量、即時編譯器編譯後的代碼。

   稱爲永久代。

   如果hotspot虛擬機確定一個類的定義信息不會被使用,也會將其回收。

   回收的基本條件至少有:所有該類的實例被回收,而且裝載該類的ClassLoader被回收

二、垃圾回收算法

    1標記-清除算法(Mark-Sweep)

從根節點開始標記所有可達對象,其餘沒標記的即爲垃圾對象,執行清除。但回收後的空間是不連續的。

    2複製算法(copying)

將內存分成兩塊,每次只使用其中一塊,垃圾回收時,將標記的對象拷貝到另外一塊中,然後完全清除原來使用的那塊內存。複製後的空間是連續的。

複製算法適用於新生代,因爲垃圾對象多於存活對象,複製算法更高效。在新生代串行垃圾回收算法中,將eden中標記存活的對象拷貝未使用的s1中,s0中的年輕對象也進入s1,如果s1空間已滿,則進入老年代;這樣交替使用s0s1。這種改進的複製算法,既保證了空間的連續性,有避免了大量的內存空間浪費。,

    3標記-壓縮算法(Mark-compact)

適合用於老年代的算法(存活對象多於垃圾對象)。

標記後不復制,而是將存活對象壓縮到內存的一端,然後清理邊界外的所有對象。

三,JVM區域總體分兩類,heap區和非heap:

1heap區又分:Eden Space(伊甸園)、Survivor Space(倖存者區)Tenured Gen(老年代-養老區)。

  非heap區又分:Code Cache(代碼緩存區)Perm Gen(永久代)、Jvm Stack(Java虛擬機棧)Local Method Statck(本地方法棧)

2HotSpot虛擬機GC算法採用分代收集算法:

 (1)、一個人(對象)出來(new 出來)後會在Eden Space(伊甸園)無憂無慮的生活,直到GC到來打破了他們平靜的生活。GC會逐一問清楚每個對象的情況,有沒有錢(此對象的引用)啊,因爲GC想賺錢呀,有錢的纔可以敲詐嘛。然後富人就會進入Survivor Space(倖存者區),窮人的就直接kill掉。

 (2)、並不是進入Survivor Space(倖存者區)後就保證人身是安全的,但至少可以活段時間。GC會定期(可以自定義)會對這些人進行敲詐,億萬富翁每次都給錢,GC很滿意,就讓其進入了Genured Gen(養老區)。萬元戶經不住幾次敲詐就沒錢了,GC看沒有啥價值啦,就直接kill掉了。

 (3)、進入到養老區的人基本就可以保證人身安全啦,但是億萬富豪有的也會揮霍成窮光蛋,只要錢沒了,GC還是kill掉。

3分區的目的:

  新生區由於對象產生的比較多並且大都是朝生夕滅的,所以直接採用標記-清理算法。而養老區生命力很強,則採用複製算法,針對不同情況使用不同算法。,

4heap區域中Perm Gen永久代中放着類、方法的定義,jvm Stack虛擬機棧區域放着方法參數、局域變量等的引用,方法執行順序按照棧的先入後出方式。


5下面對每一個內存區域做詳細介紹。

     Eden Space字面意思是伊甸園,對象被創建的時候首先放到這個區域,進行垃圾回收後,不能被回收的對象被放入到空的survivor區域。

     Survivor Space倖存者區,用於保存在eden space內存區域中經過垃圾回收後沒有被回收的對象。Survivor有兩個,分別爲To SurvivorFrom Survivor,這個兩個區域的空間大小是一樣的。執行垃圾回收的時候Eden區域不能被回收的對象被放入到空的survivor(也就是To Survivor,同時Eden區域的內存會在垃圾回收的過程中全部釋放),另一個survivor(即From Survivor)裏不能被回收的對象也會被放入這個survivor(即To Survivor),然後To Survivor Eden SpaceSurvivor Space都屬於新生代

  新生代中執行的垃圾回收被稱之爲Minor GC(因爲是對新生代進行垃圾回收,所以又被稱爲Young GC,每一次Young GC後留下來的對象age1

   注:GCGarbage Collection,垃圾回收。

      Old Gen老年代,用於存放新生代中經過多次垃圾回收仍然存活的對象,也有可能是新生代分配不了內存的大對象會直接進入老年代。經過多次垃圾回收都沒有被回收的對象,這些對象的年代已經足夠old了,就會放入到老年代。

  當老年代被放滿的之後,虛擬機會進行垃圾回收,稱之爲Major GC由於Major GC除併發GC外均需對整個堆進行掃描和回收,因此又稱爲Full GC

      heap區即堆內存,整個堆大小=年輕代大小 + 老年代大小。堆內存默認爲物理內存的1/64(<1GB);默認空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制,可以通過MinHeapFreeRatio參數進行調整;默認空餘堆內存大於70%時,JVM會減少堆直到-Xms的最小限制,可以通過MaxHeapFreeRatio參數進行調整。

 

6下面我們來認識下非堆內存(非heap區)

    Code Cache代碼緩存區,它主要用於存放JIT所編譯的代碼。CodeCache代碼緩衝區的大小在client模式下默認最大是32m,在server模式下默認是48m,這個值也是可以設置的它所對應的JVM參數爲ReservedCodeCacheSize InitialCodeCacheSize,可以通過如下的方式來爲Java程序設置。

      -XX:ReservedCodeCacheSize=128m

     CodeCache緩存區是可能被充滿的,當CodeCache滿時,後臺會收到CodeCache is full的警告信息,如下所示:

    “CompilerThread0”java.lang.OutOfMemoryError: requested 2854248 bytes for Chunk::new. Out of swap space?

注:JIT編譯器是在程序運行期間,將Java字節碼編譯成平臺相關的二進制代碼。正因爲此編譯行爲發生在程序運行期間,所以該編譯器被稱爲Just-In-Time編譯器。

      Perm Gen全稱是Permanent Generation space,是指內存的永久保存區域,因而稱之爲永久代。這個內存區域用於存放ClassMeta的信息,Class在被 Load的時候被放入這個區域。因爲Perm裏存儲的東西永遠不會被JVM垃圾回收的,所以如果你的應用程序LOAD很多CLASS的話,就很可能出現PermGen space錯誤。默認大小爲物理內存的1/64

 

四,JVM參數:

Java -Xms2g -Xmx2g -Xmn512M -Xss128K -XX:PermSize=128M -XX:MaxPermSize=128M -XX:NewRatio=4 -XX:SurivorRatio=4 -XX:MaxTenuringThreshold=1

-Xms2gJVM啓動初始化堆大小爲2gXms的默認是物理內存的1/64但小於1G

-Xmx2gJVM最大的堆大小爲2gXmx默認是物理內存的1/4但小於1G;將-Xms-Xmx的值配置爲一樣,可以避免每次垃圾回收完成後對JVM堆大小進行重新的調整。

-Xmn512M:堆中的新生代大小爲512M

-Xss128K:每個線程的堆棧大小爲128K

-XX:PermSize=128MJVM持久代的初始化大小爲128M

-XX:MaxPermSize=128MJVM持久代的最大大小爲128M

-XX:NewRatio=4JVM堆的新生代和老年代的大小比例爲14

-XX:SurvivorRatio=4:新生代Surivor區(新生代有2Surivor區)和Eden區的比例爲24

      EdenSurvivor的佔用比例。例如8表示,一個survivor區佔用 1/8 Eden內存,即1/10的新生代內存,爲什麼不是1/9因爲我們的新生代有2survivor,即S1S22所以survivor總共是佔用新生代內存的 2/10Eden與新生代的佔比則爲 8/10

-XX:MaxHeapFreeRatio  GC後,如果發現空閒堆內存佔到整個預估的比例小於這個值,則減小堆空間。

-XX:MaxTenuringThreshold=1:新生代的對象經過幾次垃圾回收後(如果還存活),進入老年代。如果該參數設置爲0,這表示新生代的對象在垃圾回收後,不進入survivor區,直接進入老年代

 

Java -XX:+UseParallelGC -XX:ParallelGCThread=4 -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

-XX:+UseParallelGC:使用並行的垃圾收集器,但僅針對新生代有效,老年代仍然使用串行收集器

-XX:ParallelGCThread=4:設置並行垃圾回收器的線程爲4個,該設置最好與處理器的數目相同

-XX:+UseParalleOldGC:配置老年代使用並行垃圾收集器,JDK1.6支持老年代使用並行收集器

-XX:MaxGCPauseMillis=100:設置每次新生代每次收集器垃圾回收的最長時間,如果無法滿足該時間,JVM會自動調整新生代區的大小,以滿足該值

-XX:+UseAdaptiveSizePolicy:設置此值後,JVM會自動調整新生代大小以及相應的surivor區的比例,以達到設置的最低響應時間或者收集頻率等

 

Java -XX:UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

-XX:UseConcMarkSweepGC:設置JVM堆的老年代使用CMS併發收集器,設置該參數後,-XX:NewRatio參數失效,但-Xmn參數依然有效

-XX:UseParNewGC:設置新生代使用併發收集器,在JDK1.5以上,JVM會根據系統自動設置

-XX:CMSFullGCsBeforeCompaction=5:設置5CMSGC後對堆空間進行壓縮、整理

-XX:+UseCMSCompactAtFullCollection:打開對老年代的壓縮,可能會影響性能,但可以消除堆碎片

提示:Heap Size 最大不要超過可用物理內存的80%,一般的要將-Xms和-Xmx選項設置爲相同,而-Xmn爲1/4的-Xmx值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章