身爲一個java程序員若是隻會使用而不知原理稱其爲初級java程序員,知曉原理而升中級、融會貫通則爲高級java
做爲有一個有技術追求的人,應當利用業餘時間及零碎時間瞭解原理程序員
近期在看深刻理解java虛擬機 第二版(基於jdk1.7)因此想寫一些觀後心得,整理一些比較重點的內容,也增強本身對重點內容的記憶!算法
如下默認虛擬機爲hotsport虛擬機jvm
一.jvm內存模型xss
① 程序計數器:spa
程序計數器爲每一個線程私有操作系統
做用能夠理解爲在虛擬機解析字節碼時記錄當前字節碼的行號線程
佔用的內存較小對象
是惟一一個不會發生內存溢出(OOM)的區域排序
②虛擬機棧:
虛擬機棧也是線程私有的
虛擬機棧包括了java虛擬機棧以及本地方法棧,二者惟一的區別是前者是針對java後者是針對本地方法(本地方法能夠理解爲操做系統的方法)
棧中的一個棧幀包含了一個方法中的局部變量、方法出口、操做數棧、動態連接等各類信息
須要注意棧中存儲的只有基本數據類型以及引用類型變量,對象的內存分配都在堆中
每一個棧幀的入棧到出棧對應着線程調用一個方法到方法執行結束
棧的深度是動態擴展的
當線程調用深度大於虛擬機容許的深度時(使用xss參數調整虛擬機棧內存容量)會發生StackOverflowError(SOF)異常
棧幀的每次入棧會判斷棧的內存容量是否足夠,發現內存不足即會申請內存,當沒法申請到足夠的內存時會發生OutOfMemoryError(OOM)異常
由於虛擬機棧嚴格遵照方法調用 入棧 方法執行結束 出棧 的規則,因此無需gc回收
③堆
java堆爲gc(Garbage Collecte11d) 回收的主要區域
java堆被全部線程共享
java堆存放着幾乎全部的對象實例,它也只存放着對象實例
堆中分爲年輕代,年老代
年輕代又被細分爲Eden區和兩個survivor區,該區的gc回收算法爲複製算法,關於gc算法下文會講到
年輕代存儲的都是新生不久的對象(未通過gc回收或者通過很少的gc回收,使用MaxTenuringThreshold參數設置通過幾回gc後對象能夠晉升到年老代)
年老代存儲的是一些佔用內存較大的大對象(使用PertenureSizeThreshold參數設定這個內存界限值)或者通過數次gc後還存活的對象
還有一種狀況,當Survivor區中同一年齡的全部對象大小總和大於Survivor區總內存的一半時,將大於等於該年齡的對象所有晉升年老代
年老代由於存儲大對象以及對象存活週期較長的特性,因此採用標記-整理算法
當有對象實例化到堆中時,若是堆中沒法提供連續的內存空間就會觸發gc,而gc後仍沒法分配足夠的內存空間就會發生OOM異常
④方法區
方法區被全部線程共享
方法區存儲着已加載的類信息、類的靜態變量、常量等數據
方法區包含了運行時常量池,其中包括了編譯期生成的各類字面量和符號引用,而且運行時常量池能夠在進程運行時動態增長數據
方法區有些時候也和年輕代和老年代並稱爲永久代,可是其實方法區並不永久,方法區也有gc回收
當常量池中的對象不存在任何引用時將被gc回收
當一個類不存在任何實例、加載該類的classLoader 已經被回收、對應的java.lang.Class 對象沒有在任何地方被引用,沒法在任何地方經過反射訪問該類的方法,才能被回收
一樣的當方法區沒法知足內存分配需求時會發生OOM異常
二.gc策略以及gc算法
在虛擬機棧以及程序計數器中由於棧幀入棧出棧的穩定性因此不須要過多考慮gc問題,這裏主要對java堆中內存gc作講解
咱們只須要明確三點就能夠理解gc的過程
1.什麼內存須要被回收
2.何時回收
3.怎樣回收
什麼內存須要被回收:固然是已經不須要再用到的對象才須要被回收,如何判斷這個對象已經無用?這裏有兩種判斷算法一是引用計數算法,二是可達性分析算法,引用計算算法就是計算各個對象之間的引用關係數量java採用的是後者,因此前者這裏很少作介紹
可達性分析算法:使用一系列對象做爲gc roots 也就是根節點遍歷整個引用鏈,在引用鏈以外的對象既無用對象
能夠做爲gc roots的對象以下:
java虛擬機棧中引用的對象
方法區中靜態變量引用的對象
方法區中常量引用的對象
本地虛擬機棧中引用的對象
這裏須要注意引用這個概念,引用分爲四種引用,四種引用排序從強至弱以下:
強引用——全部New出來的對象都爲強引用,引用鏈上的對象只要存在強引用即便發生OOM異常也沒法被gc回收
軟引用——當系統即將發生內存溢出時,會優先回收掉軟引用
弱引用——當發生gc時直接回收弱引用
虛引用——沒有任何做用,虛引用甚至不能獲取到對象實例,惟一做用是在對象被回收時會收到一個系統通知
何時回收:大多數狀況下對象都會分配在Eden區,而當Eden區內存不足時就會觸發Minor Gc
因此分配率越高,越頻繁執行 Minor GC,Minor Gc 能夠理解爲只對年輕代進行Gc回收的策略。
當年輕代Survivor空間不足,而且年老代所剩空間也沒法爲Surivivor承擔時就會發生Full Gc 能夠理解爲對所有的堆進行Gc回收
在大對象分配時老年代空間不足發生Full Gc
在方法區進行類的裝載或者常量池動態增長常量時方法區空間不足 發生Full Gc
怎樣回收:
常見的三種gc算法:標記-清除、複製、標記整理
標記-清除——先標記全部可用對象,再清除掉全部非可用對象
復 制——劃分出A、B兩片內存空間,將全部數據 存在A區域中,當發生gc時將可用對象複製到B空間,而後一次清除A空間中全部內存,下一次gc則是重複B複製到A 再清除B空間內存
標記-整理——標記全部可用對象,再將全部可用對象移動到一端,再一次性清除掉端界外的非可用對象
結合年輕代以及年老代的性質分析各個區域的gc算法
年 輕 代——由於年輕代Eden區對象絕大部分都是 「朝生夕死」 因此使用標記-清除或者標記-整理效率會很低,而使用效率高的複製算法又很是浪費空間,因此年輕代改進了複製算法,使用Eden區和兩塊Survivor區,由於Eden區一次gc存活下來的對象一般很是少,因此年輕代默認採用8:1:1的比例對Eden區和兩塊Survivor區進行內存分配,當發生gc時 將Eden區存活的對象和 一塊Survivor區中存活的對象 複製到 另外一塊Survivor區中,而後清除掉Eden 區和第一塊 Survivor區的內存,這樣既保證了gc效率,又不至於會浪費大量的內存空間
年 老 代——年老代由於存放大對象或者生命週期較長的對象,因此不能像年輕代同樣使用複製算法,由於那樣可能會致使大量的對象複製,致使效率極低,而這個時候標記-整理算法的優點體現出來,它只須要標記出 在年老代 爲數很少的 非可用對象而後將非可用對象移動到堆的末端後一次性清除便可