談談我對Java內存模型的理解

內存模型與運行時數據區

Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分成若干不同的數據區域。
Java內存模型的主要目的是定義程序中各個變量的訪問規則,在虛擬機中將變量存儲到內存和從內存中取出變量這樣的底層細節。
這裏所講述的主內存,工作內存與Java內存區域中Java堆、棧、方法區等並不是同一層次的內存劃分,這兩者基本上是沒關係的。如果兩者一定要勉強對應起來,從變量、主內存、工作內存的定義來看,主內存主要是對應Java堆中的對象實例數據部分,而工作內存則是對應了虛擬機棧中的部分內存區域。
(重點存儲數據的是堆和方法區(非堆),內存的設計也着重從這兩方面展開,這兩塊區域都是線程共享的,對於虛擬機棧、本地方法棧,程序計數器都是線程私有的)。
關於運行時數據區相關內容在我的上一篇文章中有講到
在這裏插入圖片描述

根據之前對於Heap的介紹可以知道,一般對象和數組的創建會在堆中分配內存空間,關鍵是堆中有這麼多區域,那一個對象的創建到底在哪個區域呢?

對象創建所在區域

一般情況下,新創建的對象都會被分配到Eden區,一些特殊的大對象會直接分配到Old區。

假如有對象A、B、C等創建在Eden區,但是Eden區的內存空間是有限的,假如是100M,已經使用了100M或者到達一個設定的臨界值,這時候就需要對Eden區內存空間進行清理,也就是垃圾回收操作(Garbage Collect),稱爲Minor GC,也就是Young區的GC。

經過Minor GC後,有些對象會被清理掉,有些對象還存活着,對於存活的對象需要將其複製到Survivor區,然後在清空整個Eden區域。

Survivor區是個啥?

Survivor區分爲兩塊,S0和S1,也可以叫From 和 To。再同一個時間點上,S0和S1只能有一個區有數據,另一個是空的。

接着上面的GC說,比如一開始只有Eden區和From區中有對象,To是空的。
此時進行一次GC操作,From區中的對象的分代年齡會+1,Eden區中所有存的活對象都會被複制到To區,From區中的還存活的對象會有兩個去處。
1.對象的分代年齡達到之前設置好的年齡閾值,會被移動Old區
2.沒有達到年齡閾值會被複制到To區
此時Eden區和From區已經被清空了(被GC的對象肯定沒了,沒有被GC的對象該去哪去哪了)
這時候From成了To,To成了From,From和To角色互換。 也就是說無論如何都要保證名爲To的Survivor區是空的。
Minor GC 會一直重複這個過程,直到To區被填滿了, 會將所有的對象複製到Old區。

Old區

一般Old區都是年齡比較大的對象,或者相對超過了某個閾值的對象
在Old區也會有GC的操作,Old區的GC稱爲Major GC,每次GC後還能存活的對象年齡也會+1,如果年齡超過了某個閾值,就會被回收。

對象的生命週期

我是一個普通的Java對象,我出生在Eden區,在Eden區我還看到和我長的很像的小兄弟,我們在Eden區中玩了挺長時間。 有一天Eden區中的人實在是太多了,我就被迫去了Survivor區的“From”區,自從去了Survivor區,我就開始漂了,有時 候在Survivor的“From”區,有時候在Survivor的“To”區,居無定所。直到我18歲的時候,爸爸說我成人了,該去社會上 闖闖了。 於是我就去了年老代那邊,年老代裏,人很多,並且年齡都挺大的,我在這裏也認識了很多人。在年老代裏,我生活了20年 (每次GC加一歲),然後被回收

常見問題 (歡迎補充)

GC都有啥呀?
  • Minor GC 新生代
  • Major GC 老年代
  • Full GC 新生代 + 老年代
爲什麼需要Survivor區,只有Eden區不行嗎?

如果沒有Survivor,Eden區每一次Minor GC,存活的對象就會被送到Old區。Old區很快會被填滿,從而觸發Major GC(Major GC一般會伴隨着Minor GC,可以看成是Full GC),Old區的內存空間大於新生代,進行一次Full GC消耗的時間比MInor GC長得多。

那是不是對Old區的空間進行增加或者減少就可以了?
假如增加了Old區的空間, 更多的存活對象才能填滿Old區,雖然可以降低Full GC的頻率,但是隨着Old區空間的增大,一旦發生Full GC ,執行所需要的時間就會更長。
所有Survivor的存在意義,就是減少被送到Old區的對象, 進而減少Full GC的發生,Survivor的預篩保證,只有經歷16的Minor GC還能在新生代中存活的對象,纔會被送到Old區。

爲什麼需要兩個Survivor區?

最大的好處就是解決了碎片化。
假設一下只有一個Survivor區:
剛剛新建的對象在Eden區,一但Eden滿了,觸發一次MInor GC , Eden區中存活對象就會被移動到Survivor區,這樣繼續循環下去,下一次Eden滿了的時候(問題點),此時進行MInor GC , Eden和Survivor各自都有一些存活的對象, 如果此時把Eden區的存活對象硬方到Survivor區,很明顯這兩部分對象所佔用的內存不是連續的,也就導致了內存的碎片化。
永遠有一個Survivor區是空的,另一個非空的Survivor區無碎片。

新生代中Eden : S0 : S1 爲什麼是 8:1:1

新生代中的可用內存: 複製算法用來擔保的內存爲 9:1
可用內存中的Eden:S0區爲8:1
新生代中Eden:S0:S1 = 8:1:1

P70 複製算法

現代的商業虛擬機都採用這種收集算法來回收新生代,IBM公司專門研究表明,新生代額對象大概98%是“朝生夕死,九死一生”的,所以並不需要按照1:1的比例來劃分內存空間,而是將新生代內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor,當回收時,將Eden和Survivor中還存活着的對象一次性的複製到另一塊Survivor空間上,最會清理掉Eden和剛纔用過的Survivor空間。HotSpot虛擬機默認Eden和Survivor的大小比例就是8:1,也就是每次新生代中可用的內存空間爲整個新生代容器的90%,如果新生代經過回收後,存活的對象超過10%,這樣就導致另一塊Survivor空間不足以存放存活對象,此時,這些對象直接通過分配擔保機制進入老年代。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章