JVM速成大法
《我想進大廠》之JVM奪命連環10問
《我想進大廠》之JVM奪命連環10問
看完這篇JVM垃圾回收,和面試官扯皮沒問題了
看完這篇JVM垃圾回收,和面試官扯皮沒問題了
10個經典又容易被人疏忽的JVM面試題
10個經典又容易被人疏忽的JVM面試題
炸了!一口氣問了我18個JVM問題!
炸了!一口氣問了我18個JVM問題!
空投十個JVM核心知識點,速度撿包
大吉大利 :空投十個JVM核心知識點,速度撿包
終于搞懂了Java8的記憶體結構,再也不糾結方法區和常量池了!
終于搞懂了Java8的記憶體結構,再也不糾結方法區和常量池了!
深度揭秘垃圾回收底層,這次讓你徹底弄懂她
深度揭秘垃圾回收底層,這次讓你徹底弄懂她
20張圖助你了解JVM運作時資料區,你還覺得枯燥嗎?
20張圖助你了解JVM運作時資料區,你還覺得枯燥嗎?
垃圾回收算法
新生代和老年代的垃圾回收政策
标記清除
标記-清除算法将垃圾回收分為兩個階段:标記階段和清除階段。
在标記階段首先通過根節點(GC Roots),标記所有從根節點開始的對象,未被标記的對象就是未被引用的垃圾對象。然後,在清除階段,清除所有未被标記的對象。
複制算法
從根集合節點進行掃描,标記出所有的存活對象,并将這些存活的對象複制到一塊兒新的記憶體(圖中下邊的那一塊兒記憶體)上去,之後将原來的那一塊兒記憶體(圖中上邊的那一塊兒記憶體)全部回收掉
标記整理
複制算法的高效性是建立在存活對象少、垃圾對象多的前提下的。
這種情況在新生代經常發生,但是在老年代更常見的情況是大部分對象都是存活對象。如果依然使用複制算法,由于存活的對象較多,複制的成本也将很高。
分代收集算法
分代收集算法就是目前虛拟機使用的回收算法,它解決了标記整理不适用于老年代的問題,将記憶體分為各個年代。一般情況下将堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區之外還有一個代就是永久代(Permanet Generation)。
在不同年代使用不同的算法,進而使用最合适的算法,新生代存活率低,可以使用複制算法。而老年代對象存活率搞,沒有額外空間對它進行配置設定擔保,是以隻能使用标記清除或者标記整理算法。
面試官問為什麼新生代不用标記清除算法
面試官問為什麼新生代不用标記清除算法
CMS與G1的差別
CMS收集器是一種以擷取最短回收停頓時間為目标的收集器。基于“标記-清除”算法實作,它的運作過程如下:
- 初始标記
- 并發标記
- 重新标記
-
并發清除
初始标記、從新标記這兩個步驟仍然需要“stop the world”,初始标記僅僅隻是标記一下GC Roots能直接關聯到的對象,熟讀很快,并發标記階段就是進行GC Roots Tracing,而重新标記階段則是為了修正并發标記期間因使用者程式繼續運作而導緻标記産生表動的那一部分對象的标記記錄,這個階段的停頓時間一般會比初始标記階段稍長點,但遠比并發标記的時間短。
CMS是一款優秀的收集器,主要優點:并發收集、低停頓。
缺點:
- CMS收集器對CPU資源非常敏感。在并發階段,它雖然不會導緻使用者線程停頓,但是會因為占用了一部分線程而導緻應用程式變慢,總吞吐量會降低。
- CMS收集器無法處理浮動垃圾,可能會出現“Concurrent Mode Failure(并發模式故障)”失敗而導緻Full GC産生。
浮動垃圾:由于CMS并發清理階段使用者線程還在運作着,伴随着程式運作自然就會有新的垃圾不斷産生,這部分垃圾出現的标記過程之後,CMS無法在當次收集中處理掉它們,隻好留待下一次GC中再清理。這些垃圾就是“浮動垃圾”。
- CMS是一款“标記--清除”算法實作的收集器,容易出現大量空間碎片。當空間碎片過多,将會給大對象配置設定帶來很大的麻煩,往往會出現老年代還有很大空間剩餘,但是無法找到足夠大的連續空間來配置設定目前對象,不得不提前觸發一次Full GC。
G1是一款面向服務端應用的垃圾收集器。G1具備如下特點:
- 并行于并發:G1能充分利用CPU、多核環境下的硬體優勢,使用多個CPU(CPU或者CPU核心)來縮短stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程執行的GC動作,G1收集器仍然可以通過并發的方式讓java程式繼續執行。
- 分代收集:雖然G1可以不需要其他收集器配合就能獨立管理整個GC堆,但是還是保留了分代的概念。它能夠采用不同的方式去處理新建立的對象和已經存活了一段時間,熬過多次GC的舊對象以擷取更好的收集效果。
- 空間整合:與CMS的“标記--清理”算法不同,G1從整體來看是基于“标記整理”算法實作的收集器;從局部上來看是基于“複制”算法實作的。
- 可預測的停頓:這是G1相對于CMS的另一個大優勢,降低停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明确指定在一個長度為M毫秒的時間片段内,
- G1運作步驟:
1、初始标記;2、并發标記;3、最終标記;4、篩選回收
//--------------------------------------------------------------------------------------------------
G1收集器的設計目标是取代CMS收集器,它同CMS相比,在以下方面表現的更出色:
- G1是一個有整理記憶體過程的垃圾收集器,不會産生很多記憶體碎片。
- CMS采用的是标記清除垃圾回收算法,可能會産生不少的記憶體碎片
- G1的Stop The World(STW)更可控,G1在停頓時間上添加了預測機制,使用者可以指定期望停頓時間。
各種GC的觸發條件
首先明确gc的發生區域
這是JDK8之前的,JDK8上沒有最右邊的Perm區。
從圖來看Minor GC發生在Eden區;Young GC發生在Eden、S0、S1區;Major GC發生在Old區,Full GC針對整個堆。
一般情況下,将Minor GC和YoungGC視為一個東西也行。
(1)Minor GC(YoungGC)的觸發條件
- 當Eden區滿時,觸發Minor GC。Eden區清空,全部轉移到Survivor區中。
(2)Full GC的觸發條件
- 調用System.gc()方法,這隻是通知或者是建議虛拟機進行Full GC,虛拟機可以根據情況選擇是否執行。
- 大對象或老齡對象在老年代中放不下時
- 方法區(1.8)不足,持久代(1.8之前)不足
當然,虛拟機在進行minorGC之前會判斷老年代最大的可用連續空間是否大于新生代的所有對象總空間
1、如果大于的話,直接執行minorGC
2、如果小于,判斷是否開啟HandlerPromotionFailure,沒有開啟直接FullGC
3、如果開啟了HanlerPromotionFailure, JVM會判斷老年代的最大連續記憶體空間是否大于曆次晉升(晉級老年代對象的平均大小)平均值的大小,如果小于直接執行FullGC
4、如果大于的話,執行minorGC
如何判斷一個對象是否存活
(1)引用計數法——被引用1次,則計數器加1,取消引用則減1。沒被JVM采用,因為無法解決對象之間的循環引用的問題。
(2)可達性分析算法
從以GC Roots為根節點的樹向下搜尋,能搜尋到則代表對象可用。
哪些可以作為GC Roots對象?
- 虛拟機棧(棧幀中的本地變量表)中引用的對象。
- 方法區中類靜态屬性引用的對象。
- 方法區中常量引用的對象。
- 本地方法棧中JNI(即一般說的Native方法)引用的對象。
與GC Roots不可達的對象,是立即被回收嗎?
不是立即被回收的
- 如果該對象沒有覆寫finalize方法或finalize已經被調用過,則被判定為死亡,等待回收。
- 将沒有被調用finalize的不可達對象放入一個隊列中,之後jvm建立一個低優先級的線程去挨個執行這些對象的finalize方法。如果某個對象在執行完finalize方法後,仍然沒有與引用鍊上的對象建立關系,則被判定為死亡,等待回收。如果建立了關系,則被判定為存活。
三色标記
CMS與三色标記算法
CMS與三色标記算法
與其千篇一律,不如一篇文章搞懂三色标記是如何處理漏标
與其千篇一律,不如一篇文章搞懂三色标記是如何處理漏标
三色标記算法問題講解
類加載
深刻了解java類的加載以及ClassLoader源碼分析
JVM必問知識點:類加載過程
JVM必問知識點:類加載過程
這一篇文章,可以把Java中的類加載器了解的七七八八了
這一篇文章,可以把Java中的類加載器了解的七七八八了
Java類加載器:坑爹是我的特色
Java類加載器:坑爹是我的特色
雙親委派
雙親委派機制
外婆問我:什麼是雙親委派原則?
雙親委派模型:大廠高頻面試題,輕松搞定
https://mp.weixin.qq.com/s?src=11×tamp=1618059541&ver=3000&signature=NNnNnG2bFr2xAT7IvCIZpD5hE00E5EU0taQfKNmIokTODo3quX3CDHrXtSWRbe3akZWhbhNh-yt6A0qPjI2JH4hg8L9F-LrQrFY0Sjc4fwivR36es455sZDxxCzhw-BR&new=1
讀者美團五面:Java曆史上有三次破壞雙親委派模型,是哪三次?
讀者美團五面:Java曆史上有三次破壞雙親委派模型,是哪三次?
如何打破雙親委派
Tomcat是如何打破雙親委派機制的
Tomcat是如何打破雙親委派機制的 - 簡書
哪些區域會OOM
哪些地方會發生OOM
(1)堆溢出
堆在GC後,仍然放不下新建立的對象時,則會抛出“java.lang.OutOfMemoryError:Java heap space”
如果不存在記憶體洩露的話,調大-Xmx(初始堆大小),-Xmx(最大堆大小)即可
如果存在記憶體洩漏,則需要定位到導緻記憶體洩露的代碼。
(2)方法區溢出
(方法區是規範,JDK8之前的實作為永久代,JDK8及之後的實作為中繼資料區)
方法區中主要是類資訊,靜态變量等,當類過多,靜态變量過多,就會發生OOM。
越來越多的動态代理技術也會産生大量代理類,占用大量空間。
抛出的異常為“java.lang.OutOfMemoryError:Metaspace”
可以通過-XX:MaxMetaspaceSize調大中繼資料區的最大容量
(3)虛拟機棧與本地方法棧溢出
這兩個區域的差別不過是虛拟機棧為虛拟機執行Java方法服務,而本地方法棧則為虛拟機使用到的Native方法服務,在記憶體配置設定異常上是相同的。
棧上可能會出現兩個異常
- 線程請求的棧深度超過最大深度,則抛出StackOverFlowError錯誤,比如進行了一個不會停止的遞歸調用
- 如果虛拟機棧是可以動态拓展的,拓展時無法申請到足夠的記憶體,則抛出OutOfMemoryError錯誤,比如循環建立大量線程。
(4)直接記憶體溢出
直接記憶體雖然不是虛拟機運作時資料區的一部分,但既然是記憶體,就會受到實體記憶體的限制。
在JDK1.4中引入的NIO使用Native函數庫在堆外記憶體上直接配置設定記憶體,但直接記憶體不足時,也會導緻OOM。
(5)字元串常量池溢出
JDK6時,字元串常量池還存在與持久代中,不斷的調用String.intern()則會抛出“java.lang.OutOfMemoryError:PermGen space”
使用-XX:MaxPermSize限制持久代大小即可限制字元串常量池大小
PS:
- Java6時,記憶體溢出區域為永久代。因為在Java6及之前,字元串常量池在永久代中
- Java7時,記憶體溢出區域為堆中。因為在Java7時,字元串常量池被移到堆中了。
- Java8時,記憶體溢出區域仍然為堆中,不過此時已經沒有永久代了。
哪些場景會産生OOM?怎麼解決?
面試官:哪些場景會産生OOM?怎麼解決? - 掘金
關于記憶體安全問題,你應該了解的幾點!
關于記憶體安全問題,你應該了解的幾點!
new Object() jvm層面發生了什麼
- 首先去常量池尋找該類的符号應用,找不到,則執行類加載
- 在堆上配置設定記憶體,配置設定機制有指針碰撞與空閑清單
- 多線程下進行配置設定記憶體時,有cas失敗重試與本地線程配置設定緩沖區(Thread Local Allocation Buffer, TLAB)
- 将配置設定到的記憶體空間中的資料類型都 初始化為零值(不包括對象頭)
- 對對象頭進行必要的設定 ,例如這個對象是哪個類的執行個體、對象的哈希碼、對象的GC分代年齡等資訊
- 調用對象的init()方法 ,根據傳入的屬性值給對象屬性指派
- 棧中建立對象引用 ,并指向堆中剛剛建立的對象執行個體
簡單來講,就是配置設定記憶體、初始化與棧中變量指向堆,在需要單例的對象時,需要加上DCL與volatile
為什麼使用元空間替換永久代
方法區與持久帶、元空間的差別
《Java 虛拟機規範》隻是規定了有方法區這麼個概念和它的作用,并沒有規定如何去實作它。那麼,在不同的 JVM 上方法區的實作肯定是不同的了。方法區和永久代的關系很像 Java 中接口和類的關系,類實作了接口,而永久代就是 HotSpot 虛拟機對虛拟機規範中方法區的一種實作方式。 也就是說,永久代是 HotSpot 的概念,方法區是 Java 虛拟機規範中的定義,是一種規範,而永久代是一種實作,一個是标準一個是實作,其他的虛拟機實作并沒有永久代這一說法。
JDK 1.8 的時候,方法區(HotSpot 的永久代)被徹底移除了(JDK1.7 就已經開始了),取而代之是元空間,元空間使用的是直接記憶體。
https://blogs.oracle.com/poonam/about-g1-garbage-collector,-permanent-generation-and-metaspace
下面是一些常用參數:
-XX:MetaspaceSize=N //設定 Metaspace 的初始(和最小大小)
-XX:MaxMetaspaceSize=N //設定 Metaspace 的最大大小
與永久代很大的不同就是,如果不指定大小的話,随着更多類的建立,虛拟機會耗盡所有可用的系統記憶體。
為什麼要将永久代 (PermGen) 替換為元空間 (MetaSpace) 呢?
https://plumbr.io/handbook/garbage-collection-in-java
1.整個永久代有一個 JVM 本身設定固定大小上限,無法進行調整,而元空間使用的是直接記憶體,受本機可用記憶體的限制,雖然元空間仍舊可能溢出,但是比原來出現的幾率會更小。
當你元空間溢出時會得到如下錯誤: java.lang.OutOfMemoryError: MetaSpace
你可以使用
-XX:MaxMetaspaceSize
标志設定最大元空間大小,預設值為 unlimited,這意味着它隻受系統記憶體的限制。
-XX:MetaspaceSize
調整标志定義元空間的初始大小如果未指定此标志,則 Metaspace 将根據運作時的應用程式需求動态地重新調整大小。
2.元空間裡面存放的是類的中繼資料,這樣加載多少類的中繼資料就不由
MaxPermSize
控制了, 而由系統的實際可用空間來控制,這樣能加載的類就更多了。
3.在 JDK8,合并 HotSpot 和 JRockit 的代碼時, JRockit 從來沒有一個叫永久代的東西, 合并之後就沒有必要額外的設定這麼一個永久代的地方了。
主要進行 gc 的區域是堆,就 HotSpot 虛拟機來說,永久代會發生 gc (full gc),但是,元空間使用的是直接記憶體不會發生 gc。
JVM 為什麼使用元空間替換了永久代?
面試官 | JVM 為什麼使用元空間替換了永久代? - 哔哩哔哩
ZGC
新一代垃圾回收器ZGC的探索與實踐
新一代垃圾回收器ZGC的探索與實踐 - 美團技術團隊
美團面試官問我:ZGC 的 Z 是什麼意思?
美團面試官問我:ZGC 的 Z 是什麼意思?
why哥帶你看看ZGC到底是個什麼鬼玩意?
why哥帶你看看ZGC到底是個什麼鬼玩意?
GC日志
一個簡單案例,帶你看懂GC日志
一個簡單案例,帶你看懂GC日志
JVM日志參數十全大補丸
JVM日志參數十全大補丸
垃圾回收-實戰篇
垃圾回收-實戰篇
對象的配置設定位置
對象都是配置設定在堆區嗎
Java對象都是在堆上配置設定記憶體嗎? - 簡書
對象并不一定都是在堆上配置設定記憶體的
對象并不一定都是在堆上配置設定記憶體的。
面試官問我平時寫的Bug的存儲位置(逃逸分析、标量替換、鎖消除)
https://mp.weixin.qq.com/s?src=11×tamp=1618059282&ver=3000&signature=hFx4rq5PvAgiLrieF0CQU*RuFKHHzo-IfuifTcospUvyZTNmVO-4fwenWKIgTXZEPvFhZV172kpOoCXKQy6-a8F282b0NpoL-PeMvM8sL6qkOvZF3jCivDZgLk*QX98S&new=1
JDK 1.8 下的 java.lang.Class 對象和 static 成員變量在堆還是方法區?
常量池都有哪些
class常量池、字元串常量池和運作時常量池的差別
Java中幾種常量池的區分
對象的大小
聊聊Java對象在記憶體中的大小
JOL工具分析java對象大小
JOL工具分析java對象大小 - 簡書