Java開發有個很基礎的問題,雖然咱們平時接觸的很少,可是瞭解它卻成爲Java開發的必備基礎——這就是JVM。在C++中咱們須要手動申請內存而後釋放內存,不然就會出現對象已經再也不使用內存卻仍被佔用的狀況。在Java中JVM內置了垃圾回收的機制,幫助開發者承擔對象的建立和釋放的工做,極大的減輕了開發的負擔。那是否是咱們就不須要了解JVM了,顯然在作一些優化或者深刻研究應用性能的時候,JVM仍是起了很關鍵的做用的。所以本篇就總結性的描述下JVM的內存模型與垃圾回收相關的知識。html
本文的主要內容以下:java
這幾個存儲區最主要的就是棧區和堆區,那麼什麼是棧什麼是堆呢?說的簡單點,棧裏面存放的是基本的數據類型和引用,而堆裏面則是存放各類對象實例的。
算法
堆與棧分開設計是爲何呢?多線程
棧的大小能夠經過-XSs設置,若是不足的話,會引發java.lang.StackOverflowError的異常併發
線程私有,生命週期與線程相同。每一個方法執行的時候都會建立一個棧幀(stack frame)用於存放 局部變量表、操做棧、動態連接、方法出口。性能
存放對象實例,全部的對象的內存都在這裏分配。垃圾回收主要就是做用於這裏的。優化
所以通常都建議把這兩個參數設置成同樣大,能夠避免JVM在不斷調整大小。spa
這裏記錄了線程執行的字節碼的行號,在分支、循環、跳轉、異常、線程恢復等都依賴這個計數器。線程
類型信息、字段信息、方法信息、其餘信息設計
總結
名稱 | 特徵 | 做用 | 配置 | 異常 |
---|---|---|---|---|
棧區 | 線程私有,使用一段連續的內存空間 | 存放局部變量表、操做棧、動態連接、方法出口 | -XSs | StackOverflowError OutOfMemoryError |
堆 | 線程共享,生命週期與虛擬機相同 | 保存對象實例 | -Xms -Xmx -Xmn | OutOfMemoryError |
程序計數器 | 線程私有、佔用內存小 | 字節碼行號 | 無 | 無 |
方法區 | 線程共享 | 存儲類加載信息、常量、靜態變量等 | -XX:PermSize -XX:MaxPermSize | OutOfMemoryError |
有兩種方式,一種是引用計數(可是沒法解決循環引用的問題);另外一種就是可達性分析。
判斷對象能夠回收的狀況:
這種方法優勢就是減小停頓時間,可是缺點是會形成內存碎片。
這種方法不涉及到對象的刪除,只是把可用的對象從一個地方拷貝到另外一個地方,所以適合大量對象回收的場景,好比新生代的回收。
這種方法能夠解決內存碎片問題,可是會增長停頓時間。
最後的這種方法是前面幾種的合體,即目前JVM主要採起的一種方法,思想就是把JVM分紅不一樣的區域。每種區域使用不一樣的垃圾回收方法。
上面能夠看到堆分紅三個區域:
這裏能夠詳細的說一下新生代複製回收的算法流程:
在新生代中,分爲三個區:Eden, from survivor, to survior。
這種收集器就是以單線程的方式收集,垃圾回收的時候其餘線程也不能工做。
以多線程的方式進行收集
大體的流程爲:初始標記--併發標記--從新標記--併發清除
大體的流程爲:初始標記--併發標記--最終標記--篩選回收
JVM裏的GC(Garbage Collection)的算法有不少種,如標記清除收集器,壓縮收集器,分代收集器等等,詳見HotSpot VM GC 的種類
如今比較經常使用的是分代收集(generational collection,也是SUN VM使用的,J2SE1.2以後引入),即將內存分爲幾個區域,將不一樣生命週期的對象放在不一樣區域裏:young generation,tenured generation和permanet generation。絕大部分的objec被分配在young generation(生命週期短),而且大部分的object在這裏die。當young generation滿了以後,將引起minor collection(YGC)。在minor collection後存活的object會被移動到tenured generation(生命週期比較長)。最後,tenured generation滿以後觸發major collection。major collection(Full gc)會觸發整個heap的回收,包括回收young generation。permanet generation區域比較穩定,主要存放classloader信息。
young generation有eden、2個survivor 區域組成。其中一個survivor區域一直是空的,是eden區域和另外一個survivor區域在下一次copy collection後活着的objecy的目的地。object在survivo區域被複制直到轉移到tenured區。
咱們要儘可能減小 Full gc 的次數(tenured generation 通常比較大,收集的時間較長,頻繁的Full gc會致使應用的性能收到嚴重的影響)。
堆內存GC
JVM(採用分代回收的策略),用較高的頻率對年輕的對象(young generation)進行YGC,而對老對象(tenured generation)較少(tenured generation 滿了後才進行)進行Full GC。這樣就不須要每次GC都將內存中全部對象都檢查一遍。
非堆內存不GC
GC不會在主程序運行期對PermGen Space進行清理,因此若是你的應用中有不少CLASS(特別是動態生成類,固然permgen space存放的內容不只限於類)的話,就極可能出現PermGen Space錯誤。
內存申請、對象衰老過程
1、內存申請過程
2、對象衰老過程
GC類型 | 觸發條件 | 觸發時發生了什麼 | 注意 | 查看方式 |
YGC | eden空間不足 | 清空Eden+from survivor中全部no ref的對象佔用的內存 從新調整Eden 和from的大小(parallel GC會觸發此項) |
全過程暫停應用 是否爲多線程處理由具體的GC決定 |
jstat –gcutil gc log |
FGC | old空間不足 |
清空heap中no ref的對象 permgen中已經被卸載的classloader中加載的class信息 如配置了CollectGenOFirst,則先觸發YGC(針對serial GC) 如配置了ScavengeBeforeFullGC,則先觸發YGC(針對serial GC) |
全過程暫停應用 是否爲多線程處理由具體的GC決定 是否壓縮須要看配置的具體GC |
jstat –gcutil gc log |
參考 https://www.cnblogs.com/xing901022/p/7725961.html
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html