java虛擬機管理的內存分爲五大區域:
方法區、堆 ;虛擬機棧、本地方法棧、程序計數器
程序計數器:線程私有、記錄當前線程的行號指示器,內存模型中惟一沒有OOM錯誤的區域
虛擬機棧:方法執行時建立,存儲局部變量表、操做數棧、動態連接、方法返回地址;先進後出;局部變量表的大小在編譯時期就確認了;當請求的棧深度大於當前的棧深度時,報StackOverflowError;棧空間能夠動態擴展,當沒法申請到足夠的空間時,報OOM錯誤。
本地方法棧:與虛擬機棧相似,虛擬機棧執行java方法,本地方法棧執行native方法,底層使用c或者c++編寫。
堆:java虛擬機內存中最大的一塊內存區域,存放發對象爲線程共享。全部的對象實例及數組都要在堆上分配內存,可是隨着JIT編譯器的發展和逃逸分析技術的成熟,這個說法也不是那麼絕對,可是大多數狀況都是這樣的。可能會報OOM錯誤。
JIT編譯器:能夠把java字節碼轉換爲能夠直接發送給處理器的指令。
逃逸分析:經過逃逸分析來決定哪些實例或者變量要在堆中進行分配,哪些能夠直接在棧上進行分配。這些變量的指針能夠被全局引用也能夠被其餘線程引用。
方法區:線程共享;存儲類信息、常量、靜態變量、運行時常量池(1.7移除);1.7以前可經過-XX:PermSize和-XX:MaxPermSize限制方法區大小。1.8真正開始廢棄永久代,而使用元空間(Metaspace)
對象在堆中的佈局分爲:對象頭、實例數據、對齊填充
對象頭(Markword):存儲hash碼、分代年齡、鎖標誌位(無鎖狀態、偏向鎖、輕量級鎖、重量級鎖);
棧中局部變量表中的對象引用存儲的是
堆中句柄池中的句柄地址,在對象被移動後只須要改變句柄中的實例數據的指針,無需改變ref自己。
若是直接存儲對象的實例數據,優勢就是速度快,少了一次指針定位的開銷時間
GC:java堆、方法區須要垃圾回收
堆回收區域:新生代(Eden,from survivor,to survivor)、老年代
判斷對象是否存活算法:
1.引用計數算法 優勢:效率高,實現簡單 缺點:循環引用的問題
2.可達性分析算法
經過「GC Roots」的對象做爲起始點,搜索通過的路徑稱爲引用鏈,當一個對象到GC roots沒有任何引用跟它鏈接則證實對象是不可用的。
能夠做爲GC ROOTS的對象有四種:
1.棧幀中的本地變量表中的引用對象,就是平時所說的java對象,存放在堆中
2.方法區中的類靜態屬性引用的對象,也就是static修飾引用的對象,加載類時就加載到內存中
3.方法區中的常量引用的對象
4.本地方法棧中JNI(native方法)引用的對象
要真正宣告對象死亡需通過兩個過程:
1.可達性分析後沒有發現引用鏈
2.查看對象是否有
finalize方法,若是有重寫且在方法內完成自救[好比再創建引用],仍是能夠搶救一下,注意這邊一個類的finalize只執行一次,這就會出現同樣的代碼第一次自救成功第二次失敗的狀況。[若是類重寫finalize且還沒調用過,會將這個對象放到一個叫作F-Queue的序列裏,這邊finalize不承諾必定會執行,這麼作是由於若是裏面死循環的話可能會時F-Queue隊列處於等待,嚴重會致使內存崩潰,這是咱們不但願看到的。]
垃圾收集算法:
三大垃圾收集算法:
1.標記/清除算法
2.複製算法
3.標記/整理算法
jvm採用‘分代收集算法’對不一樣區域採用不一樣的回收算法
新生代採用複製算法 :Eden from survivor to survivor 分紅8:1:1 ,to survivor爲保留空間;GC開始時,對象只會存在於Eden和from survivor區域,Eden全部存活的對象會複製到To survivor中,from survivor存活的對象會根據他們的年齡值決定去向,年齡超過年齡閾值(默認15),則移動到老年代,沒有則移動到To survivor。
老年代採用標記/清除算法或者標記/整理算法:
GC的標記階段須要STW(stop the world),讓全部的java線程掛起,這樣JVM才能安全地標記對象
OopMap能夠幫助HotSpot快速且準確完成GC Roots枚舉以及肯定相關信息。
GC不是在任意位置均可以進入,只能在safepoint處才能進入,safepoint不能太少,不然gc等待的時間會好久,也不能太多,不然將增長gc的負擔。
safepoint主要存放在:
1.循環的末尾
2.方法臨返回前
3.調用方法的call 指令後
4.可能拋出異常的位置
垃圾收集器:
若是說垃圾回收算法是內存回收的方法論,name垃圾收集器就是具體實現。JVM會結合針對不一樣的場景及用戶的配置使用不一樣的收集器。
年輕代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:Serial Old,parallel Old CMS收集器
特殊收集器:G1收集器
Serial:單線程、收集時必須停掉其它線程,等待收集工做完成後其它線程才能繼續工做,client模式使用
ParNew收集器:serial的多線程版
Parallel Scavenge收集器:採用複製算法,支持多線程,吞吐量優先,適合後臺運算
Serial Old:單線程,標記整理算法,client模式使用
Parallel Old:多線程,標記整理算法,吞吐量優先
CMS:多線程,支持併發,標記清除算法,最短回收停頓時間優先
它的運做分爲4個階段:
1.初始標記:標記一下GC ROOTS 能直接關聯到的對象,速度很快,須要STW
2.併發標記:可達性分析
3.從新標記:修正因併發標記期間用戶程序運做而產生變更的那一本部分對象的標記記錄,會有些許停頓,須要STW,時間上通常 初始標記 < 從新標記 < 併發標記
4.併發清除
CMS缺點:
1.cms性能很容易受到cpu核數的影響
2.cms沒法處理浮動垃圾(併發清除過程當中產生的新垃圾)
3.cms採用標記清除算法,會存在垃圾碎片的問題
G1收集器:
G1(garbage first:儘量多的收集垃圾,避免full gc
),最短回收停頓時間優先,強化了分區,弱化了分代的概念,是區域化、增量式的收集器,它不屬於新生代和老年代收集器;
用到的算法爲標記-清除、複製算法,jdk1.7和jdk1.8都是默認關閉的。g1是區域化的,它將java堆劃分爲若干個大小相同的區域(region),jvm能夠設置每一個region的大小(必須是2的冪),它會根據當前堆內存分配合理的region大小。
特色:
1.不會等內存耗盡或者快耗盡的時候開始垃圾收集,而是在內部才用了啓發式算法,在老年代找出具備高收集收益的分區進行垃圾收集。同時G1會根據用戶設置的暫停時間目標自動調全年輕代和總堆大小,暫停目標時間越短年輕代空間就越小、總空間就越大;
2.G1採用內存分區(region)的思路,將內存劃分爲一個個相等大小的內存分區,回收時則以分區爲單位回收,存活的對象複製到能一個空閒分區中。因爲都是以相等大小的分區爲單位進行操做,所以G1自然就是一種壓縮方案。
3.G1雖然也是分代收集器,可是是邏輯上的分代概念,不存在物理上的年輕代和老年代,每一個分區可能隨G1的運行在不一樣代之間切換,年輕代優化分爲Eden和survivor
4.G1的收集都是STW的,每次收集可能既收集年輕代,也收集老年代
5.region的大小在1m - 32m之間 ,必須是2的冪
6.region分區內部又被分紅了若干個大小爲512byte的卡片(card),卡片會記錄到全局卡片表中
7.垃圾收集過程也分爲:初始標記、併發標記、從新標記、清除
簡單gc日誌查看:-XX:+PrintGCDetails、jconsole 工具、jstat命令
jstat -gcutil pid:查看對應java進程gc狀況