Java堆内存、新生代和GC

一、堆内存、新生代、老年代

 Java 中的“堆”是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。

堆被划分成两个不同的区域:新生代 ( Young Gen )、老年代 ( Old Gen)。

新生代 ( Young Gen ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

堆大小 = 新生代 + 老年代。堆的大小可以通过参数 –Xms、-Xmx 来指定。

堆的内存模型大致为:

默认的新生代与老年代的比例的值为 1:2( 该值可以通过参数 –XX:NewRatio 来指定 ),  

即:新生代 = 1/3 的堆空间大小,老年代= 2/3 的堆空间大小。

其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为Survivor from 和Survivor to。

Java堆内存、新生代和GC_第1张图片
新生代和老年代


默认的,Edem : from : to = 8 : 1 : 1 ( 该值可以通过参数 –XX:SurvivorRatio 来设定 ),

即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。

二、Minor GC和Full GC

Java中的堆也是 GC 收集垃圾的主要区域。

GC 分为两种:Minor GC、Full GC ( 或称为 Major GC )。

Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法。

新生代几乎是所有Java对象出生的地方,即Java对象申请的内存以及存放都是在这个地方。Java中的大部分对象通常不需长久存活,具有朝生夕灭的性质。

当一个对象被判定为 "死亡" 的时候,GC 就有责任来回收掉这部分对象的内存空间。新生代是 GC 收集垃圾的频繁区域。

当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳

( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1。

以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。

但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。

Full GC 是发生在老年代的垃圾收集动作,所采用的是标记-清除算法。

现实的生活中,老年代的人通常会比新生代的人 "早死"。堆内存中的老年代(Old)不同于这个,老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 "死掉" 了的。因此,Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长。

另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。

三、测试GC

1、在Eclipse编写好一段测试代码。

       其中System.gc()方法的作用只是提醒虚拟机:希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行,而且具体什么时候进行是取决于具体的虚拟机的,不同的虚拟机有不同的对策。经过测试,这段代码的gc()会执行一次Minor GC和一次Full GC。

Java堆内存、新生代和GC_第2张图片

2、右键"Run As..."的子菜单,进入配置VM arguments,配置打印GC的详情。

Java堆内存、新生代和GC_第3张图片

3、右键Run运行,然后查看控制台的输出结果。

可以看到,GC(Minor GC)使得PSYoungGen新生代得到了部分回收(1229K->600K);

而Full GC使得PSYoungGen减小到0(600K->0K),同时老年代扩大到512K(8K->512K)。

当 Full GC 进行的时候,默认的方式是尽量清空新生代 ( PSYoungGen ),因此在调 System.gc() 时,新生代 ( PSYoungGen ) 中存活的对象会提前进入老年代。

在效率方面,Minor GC耗时几乎只有Full GC的1/10;在实际应用中,频繁Full GC比较容易造成应用程序的卡顿。

Java堆内存、新生代和GC_第4张图片

参考资料:

https://www.cnblogs.com/junwangzhe/p/6282550.html

https://www.cnblogs.com/lytwajue/p/7120031.html

https://www.cnblogs.com/lytwajue/p/7120031.html

你可能感兴趣的:(Java堆内存、新生代和GC)