Java堆詳解


Java堆

堆的對象管理

  • 在《Java虛擬機規范》中堆Java堆的描述是:所有對象實例以及數組都應該在運行時分配到堆上
  • 但是從實際使用的角度來看,不是絕對,存在某些特殊情況下的對象產生不在堆上奉陪內存
  • 這里注意,規范上是絕對,實際上是相對
  • 方法結束后,堆中的對象不會馬上被移除,需要通過GC執行垃圾回收后才會回收

堆的概述

  1. 一個JVM進程存在一個堆內存,堆是JVM內存管理的核心區域
  2. Java堆區在JVM啟動時被創建,其空間大小也被確定,是JVM管理的最大一塊內存(堆的大小可以調整)
  3. 本質上堆是一組在物理上不連續的內存空間,但是邏輯上是連續的空間(參考HSDB分析的內存結構)
  4. 所有線程共享堆,但是堆內對於線程處理還是做了一個線程私有的部分(TLAB)

堆內存的細分

  • java7之前內存邏輯划分為 新生代 + 養老區 + 永久區
  • java8之后內存邏輯划分為 **新生代 + 養老區 + 元空間
  • 新生代也可以叫年輕代,它又分為Eden區和survivor區,其中survivor區又分為 survivor0區和survivor1區 或者 From區和To區
  • 實際上不管永久代與元空間其實都是指方法區中對於長期存在的常量對象的保存

堆空間的分代思想

老年代,年輕代,永久代
  • 年輕代用於放置臨時對象或者生命周期短的對象
  • 老年代用於方式生命周期長的對象
  • 永久代或者元空間,用於存放常量
  • minor GC只管年輕代 , Full GC同時管理年輕代和老年代
為什么需要分代?有什么好處?
  • 經研究表明,不同對象的生命周期不一致,但是在具體的使用過程中70%-90%的對象是臨時對象
  • 分代唯一的理由是優化GC的性能,如果沒有分代,那么所有對象在一塊空間,GC想要回收掃描他就必須掃描所有的對象,分代之后,長期持有的對象可以挑出,短期持有的對象可以固定在一個位置回收,省掉很大一部分的空間利用

堆的默認大小

初始大小:電腦物理內存/ 64
最大內存大小:電腦物理內存 / 4

RunTime類

該類對應JVM的運行時數據區

long initialMemory = Runtime.getRuntime().totalMemory();
long maxMemory = Runtime.getRuntime().maxMemory();
long freeMemory = Runtime.getRuntime().freeMemory();

堆區詳解

對象在堆區中的位置變化

這里上兩張圖結構圖:

年輕代又分為Eden區和survivor區,
1.所有對象一出生都會在Eden區,並且對象年齡為0。Eden區滿了之后會觸發minor GC, minor GC只回收年輕代中需要回收的對象。每一次minor GC,若該對象沒有被回收,那么對象的年齡就會 + 1。對象到達閾值以后該對象會進入老年代(android閾值為6)
2.當老年代空間滿了以后會觸發full GC,full GC會同時回收年輕代和老年代中所有需要回收的對象

minor GC

Eden區滿了以后,會調用minor GC,該GC只會回收年輕代中需要回收的對象,然后對不可回收的對象進行標記(就是把不可回收對象的年齡 + 1),對象年齡達到閾值后對象會進入老年代空間

full GC

當老年代空間滿了以后會觸發full GC,full GC會回收年輕代和老年代中所有需要回收的對象

相關命令

jps ,查看當前所有java進程id

jstat -gc 進程id 查看

  • 結尾C代表總量

  • 結尾U代表已使用量

  • S0,S1代表survivor區的from 和 to

  • E代表Eden區

  • OC代表老年代總量

  • OU代表老年代已使用量

Eden區:

對象一創建就會位於該區

Survivor區:
  • Survivor區有兩塊,分別為Survivor0(S0)和Survivor1(S1),根據作用還可以分為From Survivor區和To Survivor區
  • 而 Survivor0 和 Survivor1 哪一塊作為From Survivor區,哪一塊作為To Survivor區,是不固定的。
  • 在同一時間內,Survivor0 和 Survivor0 兩塊區域一定有一塊是空的,非空的那塊作為From Survivor區,空的那塊作為To Survivor區
  • JVM 每次只會使用 Eden 和其中的一塊 Survivor 區域來為對象服務,所以無論什么時候,總是有一塊 Survivor 區域是空閑着的
    因此,年輕代實際可用的內存空間為 9/10 ( 即90% )的新生代空間
Survivor區的作用:
  • 保證對象內存的連續,提高GC的工作效率
對象在年輕代位置的變化:
  • 我們就只看一個對象在年輕代的位置變化:它一出生會位於Eden區,接着會不斷在Survivor0和Survivor1區切換,直到進入老年代
  1. 對於一個剛剛創建的對象A,它一開始位於Eden區

  2. 第一次minor GC觸發,如果它沒被回收,它會進入S0區,並且A年齡 + 1

  3. 第二次minor GC觸發,如果它沒被回收,它會進入S1區,並且A年齡 + 1

  4. 第三次minor GC觸發,如果它沒被回收,它會進入S0區,並且A年齡 + 1

  5. 第四次minor GC觸發,如果它沒被回收,它會進入S1區,並且A年齡 + 1

  6. 第....次 ,當A年齡達到閾值,進入老年代

  • 再來看看多個對象的位置變化
  1. Eden區滿后,觸發第一次minor GC,對Eden區進行對象回收(第一次minor GC時候survivor0和survivor2區都是空的,所以只回收Eden區),回收后Eden區中的存活對象的內存不再連續,此時將所有的存活對象移到Survivor0區中內存連續的空間
  2. Eden區又滿后,觸發第二次minor GC,對Eden區和Survivor0區進行對象回收(Survivor1區是空的,所以不用回收),回收后Eden區和survivor0區中的存活對象內存可能不再連續,此時會將所有的存活對象移動到Survivor1區中內存連續的空間
  3. Eden區再次滿了后,觸發第三次minor GC,對Eden區和Survivor1區進行對象回收(Survivor0區是空的,所以不用回收),回收后Eden區和survivor1區中的存活對象內存可能不再連續,此時會將所有的存活對象移動到Survivor0區中內存連續的空間
  4. Eden區滿,觸發第N次GC.......................
  5. 之后存活對象會不斷在Survivor0區和Survivor1區中切換,直到年齡達到閾值后進入老年代


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM