JVM內存模型及垃圾收集策略解析

JVM內存模型是Java的核心技術之一,以前51CTO曾爲你們介紹過JVM分代垃圾回收策略的基礎概念,如今不少編程語言都引入了相似Java JVM的內存模型和垃圾收集器的機制,下面咱們將主要針對Java中的JVM內存模型及垃圾收集的具體策略進行綜合的分析。java

一 JVM內存模型算法

1.1 Java棧編程

Java棧是與每個線程關聯的,JVM在建立每個線程的時候,會分配必定的棧空間給線程。它主要用來存儲線程執行過程當中的局部變量,方法的返回值,以及方法調用上下文。棧空間隨着線程的終止而釋放。StackOverflowError:若是在線程執行的過程當中,棧空間不夠用,那麼JVM就會拋出此異常,這種狀況通常是死遞歸形成的。數組

1.2 堆緩存

Java中堆是由全部的線程共享的一塊內存區域,堆用來保存各類JAVA對象,好比數組,線程對象等。服務器

1.2.1 Generationapp

JVM堆通常又能夠分爲如下三部分:編程語言

JVM堆的三部分

◆ Permspa

Perm代主要保存class,method,filed對象,這部門的空間通常不會溢出,除非一次性加載了不少的類,不過在涉及到熱部署的應用服務器的時候,有時候會遇到java.lang.OutOfMemoryError : PermGen space 的錯誤,形成這個錯誤的很大緣由就有多是每次都從新部署,可是從新部署後,類的class沒有被卸載掉,這樣就形成了大量的class對象保存在了perm中,這種狀況下,通常從新啓動應用服務器能夠解決問題。線程

◆ Tenured

Tenured區主要保存生命週期長的對象,通常是一些老的對象,當一些對象在Young複製轉移必定的次數之後,對象就會被轉移到Tenured區,通常若是系統中用了application級別的緩存,緩存中的對象每每會被轉移到這一區間。

◆ Young

Young區被劃分爲三部分,Eden區和兩個大小嚴格相同的Survivor區,其中Survivor區間中,某一時刻只有其中一個是被使用的,另一個留作垃圾收集時複製對象用,在Young區間變滿的時候,minor GC就會將存活的對象移到空閒的Survivor區間中,根據JVM的策略,在通過幾回垃圾收集後,任然存活於Survivor的對象將被移動到Tenured區間。

1.2.2 Sizing the Generations

JVM提供了相應的參數來對內存大小進行配置。正如上面描述,JVM中堆被分爲了3個大的區間,同時JVM也提供了一些選項對Young,Tenured的大小進行控制。

JVM的相關參數

◆ Total Heap

-Xms :指定了JVM初始啓動之後初始化內存

-Xmx:指定JVM堆得最大內存,在JVM啓動之後,會分配-Xmx參數指定大小的內存給JVM,可是不必定所有使用,JVM會根據-Xms參數來調節真正用於JVM的內存

-Xmx -Xms之差就是三個Virtual空間的大小

◆ Young Generation

-XX:NewRatio=8意味着tenured 和 young的比值8:1,這樣eden+2*survivor=1/9

堆內存

-XX:SurvivorRatio=32意味着eden和一個survivor的比值是32:1,這樣一個Survivor就佔Young區的1/34.

-Xmn 參數設置了年輕代的大小

◆ Perm Generation

-XX:PermSize=16M -XX:MaxPermSize=64M

Thread Stack

-XX:Xss=128K

1.3 堆棧分離的好處

呵呵,其它的先不說了,就來講說面向對象的設計吧,固然除了面向對象的設計帶來的維護性,複用性和擴展性方面的好處外,咱們看看面向對象如何巧妙的利用了堆棧分離。若是從JAVA內存模型的角度去理解面向對象的設計,咱們就會發現對象它完美的表示了堆和棧,對象的數據放在堆中,而咱們編寫的那些方法通常都是運行在棧中,所以面向對象的設計是一種很是完美的設計方式,它完美的統一了數據存儲和運行。

二 JAVA垃圾收集器

2.1 垃圾收集簡史

垃圾收集提供了內存管理的機制,使得應用程序不須要在關注內存如何釋放,內存用完後,垃圾收集會進行收集,這樣就減輕了由於人爲的管理內存而形成的錯誤,好比在C++語言裏,出現內存泄露時很常見的。Java語言是目前使用最多的依賴於垃圾收集器的語言,可是垃圾收集器策略從20世紀60年代就已經流行起來了,好比Smalltalk,Eiffel等編程語言也集成了垃圾收集器的機制。

2.2 常見的垃圾收集策略

常見的垃圾收集策略

全部的垃圾收集算法都面臨同一個問題,那就是找出應用程序不可到達的內存塊,將其釋放,這裏面得不可到達主要是指應用程序已經沒有內存塊的引用了,而在JAVA中,某個對象對應用程序是可到達的是指:這個對象被根(根主要是指類的靜態變量,或者活躍在全部線程棧的對象的引用)引用或者對象被另外一個可到達的對象引用。

2.2.1 Reference Counting(引用計數)
 
引用計數是最簡單直接的一種方式,這種方式在每個對象中增長一個引用的計數,這個計數表明當前程序有多少個引用引用了此對象,若是此對象的引用計數變爲0,那麼此對象就能夠做爲垃圾收集器的目標對象來收集。

優勢:

簡單,直接,不須要暫停整個應用

缺點:

1.須要編譯器的配合,編譯器要生成特殊的指令來進行引用計數的操做,好比每次將對象賦值給新的引用,或者者對象的引用超出了做用域等。

2.不能處理循環引用的問題

2.2.2 跟蹤收集器

跟蹤收集器首先要暫停整個應用程序,而後開始從根對象掃描整個堆,判斷掃描的對象是否有對象引用,這裏面有三個問題須要搞清楚:

JVM的跟蹤收集器

1.若是每次掃描整個堆,那麼勢必讓GC的時間變長,從而影響了應用自己的執行。所以在JVM裏面採用了分代收集,在新生代收集的時候minor gc只須要掃描新生代,而不須要掃描老生代。

2.JVM採用了分代收集之後,minor gc只掃描新生代,可是minor gc怎麼判斷是否有老生代的對象引用了新生代的對象,JVM採用了卡片標記的策略,卡片標記將老生代分紅了一塊一塊的,劃分之後的每個塊就叫作一個卡片,JVM採用卡表維護了每個塊的狀態,當JAVA程序運行的時候,若是發現老生代對象引用或者釋放了新生代對象的引用,那麼就JVM就將卡表的狀態設置爲髒狀態,這樣每次minor gc的時候就會只掃描被標記爲髒狀態的卡片,而不須要掃描整個堆。具體以下圖:
3.GC在收集一個對象的時候會判斷是否有引用指向對象,在JAVA中的引用主要有四種:Strong reference,Soft reference,Weak reference,Phantom reference.

◆ Strong Reference

強引用是JAVA中默認採用的一種方式,咱們平時建立的引用都屬於強引用。若是一個對象沒有強引用,那麼對象就會被回收。

public void testStrongReference(){  Object referent = new Object();  Object strongReference = referent;  referent = null;  System.gc();  assertNotNull(strongReference);  }

◆ Soft Reference

軟引用的對象在GC的時候不會被回收,只有當內存不夠用的時候纔會真正的回收,所以軟引用適合緩存的場合,這樣使得緩存中的對象能夠儘可能的再內存中待長久一點。

Public void testSoftReference(){  String  str =  "test";  SoftReference<String> softreference = new SoftReference<String>(str);  str=null;  System.gc();  assertNotNull(softreference.get());  }

◆ Weak reference

弱引用有利於對象更快的被回收,假如一個對象沒有強引用只有弱引用,那麼在GC後,這個對象確定會被回收。

Public void testWeakReference(){  String  str =  "test";  WeakReference<String> weakReference = new WeakReference<String>(str);  str=null;  System.gc();  assertNull(weakReference.get());  }

◆ Phantom reference

2.2.2.1 Mark-Sweep Collector(標記-清除收集器)

標記清除收集器最先由Lisp的發明人於1960年提出,標記清除收集器中止全部的工做,從根掃描每一個活躍的對象,而後標記掃描過的對象,標記完成之後,清除那些沒有被標記的對象。

優勢:

1 解決循環引用的問題

2 不須要編譯器的配合,從而就不執行額外的指令

缺點:

1.每一個活躍的對象都要進行掃描,收集暫停的時間比較長。

2.2.2.2 Copying Collector(複製收集器)複製收集器將內存分爲兩塊同樣大小空間,某一個時刻,只有一個空間處於活躍的狀態,當活躍的空間滿的時候,GC就會將活躍的對象複製到未使用的空間中去,原來不活躍的空間就變爲了活躍的空間。複製收集器具體過程能夠參考下圖:

JVM的複製收集器

優勢:

1 只掃描能夠到達的對象,不須要掃描全部的對象,從而減小了應用暫停的時間

缺點:

1.須要額外的空間消耗,某一個時刻,老是有一塊內存處於未使用狀態

2.複製對象須要必定的開銷

2.2.2.3 Mark-Compact Collector(標記-整理收集器)標記整理收集器汲取了標記清除和複製收集器的優勢,它分兩個階段執行,在第一個階段,首先掃描全部活躍的對象,並標記全部活躍的對象,第二個階段首先清除未標記的對象,而後將活躍的的對象複製到堆得底部。標記整理收集器的過程示意圖請參考下圖:Mark-compact策略極大的減小了內存碎片,而且不須要像Copy Collector同樣須要兩倍的空間。

並行方式垃圾收集

相關文章
相關標籤/搜索