如有侵权,还请告知!810497207@qq.com

一:堆内存结构

emwin 使用内部内存_老年代

 

 


1.年轻代(新生代 Young Generation)

①:Eden(伊甸园区)

②:S0

③:S1

注意:默认内存大小占比: Eden:S0:S1= 8:1:1

2.老年代(Old Generation)


注意:默认内存大小占比: 新生代:老年代 = 1:2

3.永久代

自JDK1.8之后,HotSpot JVM 去掉了永久代,取而代之的是元数据区(Metaspace),使用的是本地内存,而不是堆内存


二:堆内存垃圾回收全过程

emwin 使用内部内存_JVM_02

 

 


新创建的对象会先被分配到到Eden区。JVM刚启动时,Eden区对象数量较少,两个 Survivor区S0、S1几乎是空的。


 

emwin 使用内部内存_老年代_03

 

 

随着时间的推移,Eden区的对象越来越多。当Eden区放不下时(占用空间达到容量阈值),新生代就会发生垃圾回收,我们称之为Minor GC或者Young GC。

 

emwin 使用内部内存_老年代_04

 

 


发生GC时,

第一步会通过可达性分析算法找到可达对象。如上图,蓝色为可达对象,其 他紫色为不可达对象。

第二步,被标示的可达对象会被转移到S0(此时S0是From Survivor),此时存活对象年龄加1,三个对象年龄都变为1。

第三步,清除Eden区所有 对象。

emwin 使用内部内存_emwin 使用内部内存_05

 

 


GC后各区域对象占用情况,如上图所示。

emwin 使用内部内存_JVM_06

 

 


程 序 继 续 运 行 , Eden 区 再 次 达 到 容 量 阈 值 时 , 会 再 次 发 生 GC 。 这 时 S0 ( From Survivor)已经有了对象。还是同样的步骤,通过可达性分析算法找到可达对象,然后再 将Eden和S0中的可达对象转移到S1(To Survivor),各存活对象年龄加1。最后将 Eden和S0中的所有对象清除。

 

emwin 使用内部内存_JVM_07

 

 


GC后S0区域被清空。如上图所示。S0和S1发生了互换,S1变成了From Survivor,S0 变成了To Survivor。 注意,To Survivor区永远都为空。这实际上是垃圾回收算法-复制算法在年轻代的实际应 用。把年轻代分为Eden,S0,S1三个区域,每次垃圾回收时把可达对象复制到S0或 S1,然后再清除掉Eden和(S1或S0)中的所有对象。由于每次GC时,新生代的可达对 象非常少(绝大部分对象要被回收掉),一般不会超过新生代总体空间的10%,所以搜 寻可达对象以及复制对象的成本都会非常低。而且这种复制的方式还能避免产生堆内存碎 片,提高内存利用率。很多年轻代垃圾收集器都采用复制算法,如ParNew。

emwin 使用内部内存_老年代_08

 

 


在程序运行过程中,新生代GC会反复发生,长寿对象会在S0和S1之间反复交换,年龄也 会越来越大,当对象达到年龄上限时,会被晋升到老年代。这个年龄上限默认是15,可 以通过参数-XX:MaxTenuringThreshold设置。如下图,有些年轻代对象年龄达到了上 限15,被转移到了老年代。

emwin 使用内部内存_JVM_09

 

 


其他晋升方式。

  新生代对象晋升到老年代,除了根据年龄正常晋升外。为了提高JVM的 性能,JVM设计者还考虑了其他晋升方式。

emwin 使用内部内存_emwin 使用内部内存_10

 

 


大 对 象 直 接 晋 升 。

  大 对 象 会 跨 过 年 轻 代 直 接 分 配 到 老 年 代 。 可 以 通 过 - XX:PretenureSizeThreshold参数设置对象大小。如果参数被设置成5MB,超过5MB的

大对象会直接分配到老年代。这样做的目的,是为了避免大对象在Eden区及两个 Survivor区之间大量的内存复制,大对象的内存复制耗时比普通对象要高很多。 注意:PretenureSizeThreshold参数只对Serial和ParNew两种回收器有效。

emwin 使用内部内存_JVM_11

 

 


动态对象年龄判定。

  如果在Survivor空间中相同年龄对象大小的总和大于Survivor空间 的 一 半 , 年 龄 大 于 或 等 于 该 年 龄 的 对 象 会 直 接 进 入 老 年 代 , 而 不 用 等 到 MaxTenuringThreshold中设置的年龄上限。上图,年龄为1的对象超过了Survivor空间 的一半,所以这几个对象会直接进入老年代。


实际上,上面对动态对象年龄判定的描述并不精确。上图的场景也会导致相关对象晋升到 老年代。年龄为1的对象加上年龄为2的对象超过了半数,这时包括年龄为2的对象以及年 龄更大的对象都会被晋升到老年代。所以上图中年龄为2和3的对象都会被晋升到老年 代。

emwin 使用内部内存_JVM_12

 

 

老年代垃圾回收

  随着年轻代对象的不断晋升,老年代的对象变得越来越多,达到容量阈 值后老年代也会发生垃圾回收,我们称之为Major GC或者Full GC,Full GC并不是全局GC,它只发生在老年代。

虽然年轻代和老年代都会发生GC,但是每次GC的时间和成本却大不相同。由于老年代空 间大小一般是年轻代的几倍,再加上老年代对象存活率很高,所以整个标记过程比较慢, GC成本也非常高。我们经常说的JVM调优,主要是为了尽量减少老年代Full GC的时间和 频次。

  老年代垃圾回收器,很少使用复制算法,主要为了避免大量对象的内存复制带来的时间和 空间上的开销,一般采用标记清除、标记整理算法,就地标记回收。例如,老年代垃圾收 集器CMS就采用了标记清除算法。对于标记清除算法带来的内存碎片问题,CMS提供了 两 个 参 数 做 碎 片 整 理 , -XX:+UseCMSCompactAtFullCollection 和 - XX:CMSFullGCsBeforeCompaction。