「傳說古代有一隻四角四足的怪獸:名叫
夕
。因冬天大雪致使夕沒東西吃,因此夕常常到附近的村裏找吃的,因其身體龐大、脾氣暴躁、兇猛異常,給村民帶來了很大的災難。java後來有一位聰明的孩子,他叫作
」年
,教給你們除掉「夕」的方法:用爆竹,輕則趕走它,重則傷它。每一年臘月三十
,夕都會來村裏,村名就守着夜
,放着鞭炮趕走夕。除夕
由此而來。程序員
咱們把「夕」想象成一個不斷吃機器內存的 Jav a 程序,就稱它爲年獸
吧。 掌管 Java 虛擬機內存的就是「年」,咱們稱它爲年哥
吧。
年獸的地盤
年哥
管理的地盤主要分爲五大區:堆、方法區、虛擬機棧、本地方法棧、程序計數器。以下圖所示。另外你們能夠把圖中的線程想象成村民,而堆是做爲村民共享使用的區域。算法
![](http://static.javashuo.com/static/loading.gif)
運行時數據區小程序
堆又能夠進行細分,分爲新生代
和老年代
,新生代和老年代的比例是 1:2,而新生代又能夠進行細分,分爲伊甸園(Eden)區和兩個 Survivor
, 其中 Eden
區大小和 Survivor
區大小是 8:1。數組
以下圖所示,年獸和村民都是共享
堆內存這塊地盤的,管理員年哥是管理堆內存的。其中的數字 一、八、20 分別表明佔用內存的份數。微信
![](http://static.javashuo.com/static/loading.gif)
共享堆區架構
年獸的胃口
年獸的胃口是村民的幾百倍
,年獸假扮村民逃過了管理員年哥的檢查,年哥對於這種大胃王
都是直接分配到老年代
去的,由於大胃王須要連續的內存給它吃,而新生代的碎片比較多不知足條件。在 Java 的世界中,最典型的大胃王就是大對象:如很長的字符串,或者元素數量很龐大的數組。分佈式
以下圖所示,村民分配到新生代吃內存,年獸被直接分配到老年代。微服務
![](http://static.javashuo.com/static/loading.gif)
年獸被直接分配到老年代性能
大量年獸入侵
年獸嚐到甜頭後,就開始不斷地呼叫它的親戚朋友,大量年獸被分配到了老年代,直接致使老年代的內存空間不足了,以下圖所示:
![](http://static.javashuo.com/static/loading.gif)
大量年獸入侵
代碼演示
咱們用代碼來演示下年獸入侵:
-
建立了 3 個年獸,都佔用 10 MB 內存。
public class SpringFestivalOOM { public static void main(String[] args) { // 年獸1/2/3,都佔用 10 MB 內存 byte[] nianShou1 = new byte[10 * 1024 * 1024]; byte[] nianShou2 = new byte[10 * 1024 * 1024]; byte[] nianShou3 = new byte[10 * 1024 * 1024]; } }
-
編譯這段程序。
javac SpringFestivalOOM.java
-
執行這段程序,同時設置堆內存最大爲 20 MB。
java -Xms20M -Xmx20M SpringFestivalOOM
由於 3 個年獸佔用的內存 30 MB 大於堆的最大內存 20 MB,因此拋出堆內存溢出異常,以下圖所示:
![](http://static.javashuo.com/static/loading.gif)
堆內存溢出異常
這個時候年哥和村民才發現,原來有這麼多年獸佔了咱們的地盤,趕快消滅它們!
打走年獸
村民們和年哥湊到一塊,討論了下該如何解決這個問題,究其緣由就是年獸太多了,要減小他們呼朋喚友來吃內存。
放到咱們的 Java 世界中,就是減小大對象的頻繁建立。
咱們程序員常常出現本地寫完代碼後沒什麼問題,到線上後就出問題,極可能的緣由就是線上環境的數據量大,很容易出現大對象的頻繁建立,好比大型促銷活動時,短期
內須要建立大量訂單數據,而訂單數據又比較複雜,有不少字段,可能會佔用大量的內存空間,最終致使頻繁觸發垃圾回收
,而垃圾回收時又會出現 Stop the world
現象,應用程序的性能就降下來了。
守歲
在除夕晚上,都會進行「守歲」,村民們齊聚一堂吃着年夜飯,一塊兒等待除夕的鐘聲。等到天亮再拜訪親戚鄰居。
而守歲這個過程只能待在家裏,不能作其餘事情,因此能夠當作是垃圾回收時,其餘線程不能工做,也就是 Stop the world 的由來。
以下圖所示,除夕以前,村民能夠去其餘地方活動,除夕夜就只能待在家裏守歲了,到了次日早上就能夠串門拜年了。
![](http://static.javashuo.com/static/loading.gif)
守歲
總結
本篇經過除夕的故事來說解 Java 中垃圾回收機制,因故事較爲簡單,因此並無對垃圾回收算法進行深刻講解,本篇只能算做垃圾回收的入門,但願能給你們帶來必定啓發做用,對 JVM 很熟的同窗就當學習下除夕的來歷吧~
-
村民做爲小對象使用堆區的 新生代,年獸做爲大對象直接使用堆區的 老年代。
-
除夕當晚,大量 年獸入侵老年代,致使 堆區內存不足,觸發 垃圾回收機制。
-
守歲就是待在家裏守着過新年,而垃圾回收時, 又會中止其餘線程,也就是 Stop the world。
-
避免代碼中 頻繁複制或建立大對象是必須作的事情,以避免上線後出現問題。
-
除夕也表明着 辭舊迎新 ,這不正是執行垃圾回收嗎?
做者簡介
:8 年互聯網經驗,擅長架構設計、分佈式、微服務。手寫了一套 SpringCloud 實戰教程,自主開發了 PMP 刷題小程序和 Java 刷題小程序。回覆 pdf 領取。
「悟空在這裏預估你們除夕快樂,新年快樂,2021 心想事成,好事成雙~
」
本文分享自微信公衆號 - 悟空聊架構(PassJava666)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。