一、Java 堆概念
1、簡介
對于Java應用程式來說, Java堆(Java Heap) 是虛拟機所管理的記憶體中最大的一塊。 Java堆是被所 有線程共享 的一塊記憶體區域, 在虛拟機啟動時建立。 此記憶體區域的唯一目的就是存放對象執行個體, Java 世界裡“幾乎”所有的對象執行個體都在這裡配置設定記憶體。“幾乎”是指從實作角度來看, 随着Java語 言的發展, 現在已經能看到些許迹象表明日 後可能出現值類型的支援, 即使隻考慮現在, 由于即時編譯技術的進步, 尤其是逃逸分析技術的日漸強大, 棧上 配置設定、 标量替換優化手段已經導緻一些微妙的變化悄然發生, 是以說Java對象執行個體都配置設定在堆上也漸漸變得不是 那麼絕對了。
2、堆的特點
(1)是Java虛拟機所管理的記憶體中最大的一塊。
(2)堆是jvm所有線程共享的。 堆中也包含私有的線程緩沖區 Thread Local Allocation Buffer (TLAB)
(3)在虛拟機啟動的時候建立。
(4)唯一目的就是存放對象執行個體,幾乎所有的對象執行個體以及數組都要在這裡配置設定記憶體。 (5)Java堆是垃圾收集器管理的主要區域。
(6)是以很多時候java堆也被稱為“GC堆”(Garbage Collected Heap)。從記憶體回收的角度來看,由于現在收集器 基本都采用分代收集算法,是以Java堆還可以細分為:新生代和老年代;新生代又可以分為:Eden 空間、From Survivor空間、To Survivor空間。
(7)java堆是計算機實體存儲上不連續的、邏輯上是連續的,也是大小可調節的(通過-Xms和-Xmx控制)。
(8)方法結束後,堆中對象不會馬上移出僅僅在垃圾回收的時候時候才移除。
(9)如果在堆中沒有記憶體完成執行個體的配置設定,并且堆也無法再擴充時,将會抛出OutOfMemoryError異常
3、設定堆空間大小
1. 記憶體大小-Xmx/-Xms
使用示例: -Xmx20m -Xms5m
說明: 當下Java應用最大可用記憶體為20M, 最小記憶體為5M
total Memory和最大的記憶體之間還是存在一定 差異的,就是說JVM一般會盡量保持記憶體在一個盡可能低的層面,而非貪婪做法按照最大的記憶體來進行配置設定。
其實JVM在配置設定記憶體過 程中是動态的, 按需來配置設定的。
4、堆的分類
現在垃圾回收器都使用分代理論,堆空間也分類如下:
在Java7 Hotspot虛拟機中将Java堆記憶體分為3個部分:
- 青年代Young Generation
- 老年代Old Generation
- 永久代Permanent Generation
在Java8以後,由于方法區的記憶體不再配置設定在Java堆上,而是存儲于本地記憶體元空間Metaspace中,是以永久代就不 存在了,在幾天前(2018年9約25日)Java11正式釋出以後,我從官網上找到了關于Java11中垃圾收集器的官方文檔, 文檔中沒有提到“永久代”,而隻有青年代和老年代。
二、年輕代和老年代
1.JVM中存儲java對象可以被分為兩類:
1)年輕代(Young Gen):年輕代主要存放新建立的對象,記憶體大小相對會比較小,垃圾回收會比較頻繁。年輕代分 成1個Eden Space和2個Suvivor Space(from 和to)。
2)年老代(Tenured Gen):年老代主要存放JVM認為生命周期比較長的對象(經過幾次的Young Gen的垃圾回收後仍 然存在),記憶體大小相對會比較大,垃圾回收也相對沒有那麼頻繁。
2.配置新生代和老年代堆結構占比
預設 -XX:NewRatio=2 , 辨別新生代占1 , 老年代占2 ,新生代占整個堆的1/3
修改占比 -XX:NewPatio=4 , 辨別新生代占1 , 老年代占4 , 新生代占整個堆的1/5
Eden空間和另外兩個Survivor空間占比分别為8:1:1
可以通過操作選項 -XX:SurvivorRatio 調整這個空間比例。 比如 -XX:SurvivorRatio=8 幾乎所有的java對象都在Eden區建立, 但80%的對象生命周期都很短,建立出來就會被銷毀
從圖中可以看出: 堆大小 = 新生代 + 老年代。其中,堆的大小可以通過參數 –Xms、-Xmx 來指定。
預設的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ( 該值可以通過參數 –XX:NewRatio 來指定 ),即:新生 代 ( Young ) = 1/3 的堆空間大小。老年代 ( Old ) = 2/3 的堆空間大小。其中,新生代 ( Young ) 被細分為 Eden 和 兩個 Survivor 區域,這兩個 Survivor 區域分别被命名為 from 和 to,以示區分。 預設的,Edem : from : to = 8 : 1 : 1 ( 可以 通過參數 –XX:SurvivorRatio 來設定 ),即: Eden = 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小。 JVM 每次隻會使用 Eden 和其中的一塊 Survivor 區域來為對象服務,是以無論什麼時候,總是有一塊 Survivor 區域 是空閑着的。是以,新生代實際可用的記憶體空間為 9/10 ( 即90% )的新生代空間。
三、對象配置設定過程
JVM設計者不僅需要考慮到記憶體如何配置設定,在哪裡配置設定等問題,并且由于記憶體配置設定算法與記憶體回收算法密切相關, 是以還需要考慮GC執行完記憶體回收後是否存在空間中間産生記憶體碎片。
配置設定過程
1.new的對象先放在伊甸園區。該區域有大小限制
2.當伊甸園區域填滿時,程式又需要建立對象,JVM的垃圾回收器将對伊甸園預期進行垃圾回收(Minor GC),将伊 甸園區域中不再被其他對象引用的額對象進行銷毀,再加載新的對象放到伊甸園區
3.然後将伊甸園區中的剩餘對象移動到幸存者0區
4.如果再次觸發垃圾回收,此時上次幸存下來的放在幸存者0區的,如果沒有回收,就會放到幸存者1區
5.如果再次經曆垃圾回收,此時會重新傳回幸存者0區,接着再去幸存者1區。
6.如果累計次數到達預設的15次,這會進入養老區。 可以通過設定參數,調整門檻值 -XX:MaxTenuringThreshold=N
7.養老區記憶體不足是,會再次出發GC:Major GC 進行養老區的記憶體清理
8.如果養老區執行了Major GC後仍然沒有辦法進行對象的儲存,就會報OOM異常
配置設定對象流程