劍指Offer——JVM 基礎知識點儲備
@TOC

一、前言
應聘后端開發崗位面試過程中,有關JVM的問題必不可少,此篇博文主要梳理有關JVM工作原理、收集器有關內容。
二、java 內存與內存溢出
2.1 JVM 分區及作用
程序計數器(線程私有)
當前線程執行字節碼的信號指示器。(每個線程都會在程序計數器中存儲其指令,從而實現線程切換后恢復到正確的執行位置)
虛擬機棧(棧,線程私有)
每個方法執行(開始到結束就是這個方法的生命周期)都會創建一個棧幀,棧幀存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。
(棧內存)為虛擬機執行java方法服務:方法被調用時創建棧幀–>局部變量表–>局部變量–>對象引用
如果線程請求的棧深度超出了虛擬機所允許的深度,就會出現StackOverFlowError. -Xss規定了棧的最大空間;
虛擬機棧可以動態擴展,如果擴展到無法申請到足夠的內存,會出現OOM(OutOfMemoryError)
而我們最常用的就是局部變量表,局部變量表包括如下內容:
基本數據類型: boolean byte char short int float long double
注意基本類型的包裝類型:Boolean、Byte、Character、Short、Integer、Float、Long、Double
對象引用類型:類、接口、數組 (不是對象本身,可能是一個指向對象起始地址的引用指針)
問題:包裝類型是放在棧中么:String Interget(看包裝類型是怎么用的:若直接定義則內容在常量池中,若new一個對象則在堆中。)
本地方法棧 。與虛擬機實現的功能非常相似,不同之處在于虛擬機執行java方法(字節碼)服務,而本地方法棧執行Native 方法服務(非java方法寫的)。
java 堆。(線程共享) 虛擬機啟動時創建,此內存區域的唯一目的就是存放對象實例,對象在失去引用,就會被java虛擬機回收。
被所有線程共享,在java虛擬機啟動時創建,幾乎所有的對象實例都存放到堆中;
GC管理的主要區域;
物理上不連續,邏輯上連續,并可以動態擴展,無法擴展時拋出OutOfMemoryError;
方法區(線程共享)(虛擬機把方法區叫做永久代)。
用于存儲已被虛擬機加載的類信息、常量、靜態變量、即編譯器編譯后的代碼等數據。
注意??:特別注意靜態變量static修飾的變量在方法區。
直接內存(了解即可)
不是虛擬機運行時數據區的一部分。是native函數直接分配的堆外內存,這樣避免了java堆和native堆來回復制數據。
三、垃圾收集器與內存分配策略
3.1 jvm垃圾處理方法(標記清除、復制、標記整理)
標記—清除算法
標記階段:先通過根節點,標記所有從根節點開始的對象,未被標記的視為垃圾對象;
清除階段:清除所有未被標記的對象。
復制算法
將原有的內存空間分成兩塊,每次只使用其中一塊,在垃圾回收時,將正在使用的內存中存活對象復制到未使用的內存塊中,然后清除正在使用的內存塊中所有對象。
標記—整理算法
若對象存活率比較高,就要進行多次復制,效率比較低。
標記階段:先通過根節點,標記所有從根節點開始的可達對象,未被標記的視為垃圾對象。
整理階段:將所有的存活對象壓縮到內存的一端(或向一端移動),之后清理邊界所有的空間。
分代收集算法
只是根據對象存活周期的不同將內存劃分為幾塊。一般把java堆分為新生代和老年代。
新生代大量對象死亡,只有少數對象存活,采用復制算法;
老年代對象存活率高,沒有額外空間對它進行分配,故采用標記-清除或標記-整理算法。
三種算法的比較:
效率:復制算法 > 標記-整理算法 > 標記-清除算法(此處的效率只是簡單的對比時間復雜度)
內存整理度:復制算法 = 標記-整理算法 》標記-清除算法
內存利用率:標記-整理算法 = 標記-清除算法 》復制算法
3.2 JVM如何GC?新生代,老年代,持久代,都存儲哪些東西,以及各個區的作用?
大多數新生的對象在Eden區分配,當Eden區沒有足夠空間進行分配時,虛擬機就會進行一次Minor GC。(Survivor是兩個)
1. 新生代
在方法中new一個對象,方法調用完畢后,對象就會被回收,這就是一個典型的新生代對象。(新生對象在eden區經歷過一次minorGC并且被Survivor容納的話,對象年齡為1,每一次熬過MinorGc 年齡就會加1,直到15,就會晉升到老年。)
注意動態對象的判定:Survivor空間中相同年齡的對象大小總和大于Survivo空間的一半,大于或等于該年齡的對象就可以直接進入老年代。
老年代
在新生代中經歷了N次垃圾回收后仍然存活的對象,就會被放到老年代中,而且大對象(占用大量連續內存空間的java對象如很長的字符串及數組)直接進入老年代。
當survivor空間不夠用時,需要依賴老年代進行分配擔保。
3.3 GC 引用可達性分析算法中 GCRoots 對象
java虛擬機棧中的對象(引用對象);
方法區中的靜態成員;
方法區中的常量引用對象;
本地方法區中的JNI(Native方法)引用對象 ;
3.4 MinorGC、FullGC時機
1. MinorGC(新生代GC)
當Eden區沒有足夠空間進行分配時,虛擬機就會進行一次Minor GC。
新生代中的垃圾收集動作,采用的是復制算法;
對于較大的對象(很長的字符串、數據、集合),在Minor GC的時候可以直接進入老年代。
2. FullGC(老年代GC)
Full GC 是發生在老年代的垃圾收集動作,采用的是標記-清除/整理算法;
由于老年代的對象幾乎都是在survivor區熬過來的,不會那么容易死掉,因此Full GC發生的次數不會像MInor GC那么頻繁,Full GC清理時間是Minor GC的10倍。
3.5 各垃圾回收器工作原理
是一個單線程收集器,它“單線程”的意義并不僅僅說明它只會使用一個cpu或一條線程去完成垃圾回收工作。而是在收集垃圾時,暫停其他的工作線程。
新生代采用復制算法,stop-the-world(消除或者減少工作線程因內存回收而導致停頓)。
老年代采用標記–整理算法。
簡單高效,client模式下默認的新生代收集器。
ParNew收集器是Serial收集器的多線程版本;
新生代采用復制算法,stop-the-world;
老年代采用標記–整理算法;
它是運行在server模式下首選新生代收集器;
除了serial收集器之外,只能它能和cms收集器配合工作。
類似ParNew,但是更加關注吞吐量。目標是:達到一個可控制吞吐量的收集器;
停頓時間和吞吐量不可能同時調優。我們一方面希望停頓時間少,另一方面希望吞吐量高,其實這是矛盾的。
因為:在GC的時候,垃圾回收的工作量是不變的,如果停頓時間減少,那頻率就會提高;既然頻率提高了,說明就會頻繁的進行GC,那吞吐量就會減少,性能就會降低。
是當今收集器發展的最前沿成果之一,對垃圾回收進行劃分優先級的操作,這種有優先級的區域回收方法保證了它的高效率;
最大的優點是結合了空間整合,不會產生大量的碎片,也降低了進行GC的頻率;
讓使用者明確指定停頓的時間。
一種以獲得最短回收停頓時間為目標的收集器,適用于互聯網網站或者B/S系統的服務器上;
初始化標記(stop-the-world):根可以直接關聯到的對象;
并發標記(和用戶線程一起):主要標記過程,標記全部對象;
重新標記(stop-the-world):由于并發標記時,用戶線程依然運行,因此在正式清理前,再做修正;
并發清除(和用戶線程一起):基于標記結果,直接清理對象。
注意:CMS有三個致命的問題:
cpu資源占用;
浮動的垃圾無法清除;
內存碎片;
3.6 java 引用類型
Java有四種引用類型:
強引用:通過new產生的對象都是強引用。
軟引用:一些還有用但不是必須的對象可以使用軟引用。比如創建一個軟引用數組,這個數組存放了100多個學生對象的信息。內存比較空閑的時候這些對象和強引用沒有區別,但內存緊張的時候就會被GC回收。(這就是GC的判定條件)
應用軟引用的好處:java在內存不足時,程序不會崩潰;
弱引用:描述非必須對象的(比軟引用更弱),當GC工作時,無論內存是否緊張都會回收掉;當某個對象是偶爾使用,并且在使用時隨時能獲取,又不想影響垃圾的回收,可以考慮應用這個。
虛引用:無法通過虛引用來獲取對一個對象的真實引用。唯一的用處:能在對象被GC時收到系統通知,JAVA中用PhantomReference來實現虛引用。
Java JVM
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。