java新生代、旧生代、永久代

发布时间 2023-10-07 13:22:38作者: ckxkexing

参考:https://www.cnblogs.com/crazy-lc/p/12636927.html
本文在参考的基础上,补充了GC算法的描述。

Java堆从GC的角度可以细分为:

新生代(Eden区、From Survivor区和To Survivor区)和老年代。

新生代

用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。新生代又分为Eden区、ServivorFrom、ServivorTo 3个区。

Minor GC和Major GC的区别

Minor GC:简单理解就是发生在年轻代的GC。三步(复制——清空——互换)

Minor GC的触发条件

当产生一个新对象,新对象优先在Eden区分配。如果Eden区放不下这个对象,虚拟机会使用复制算法发生一次Minor GC,清除掉无用对象。同时将存活对象移动到Survivor的其中一个区(fromspace区或者tospace区)。

为什么要设置Survivor区?目的是让中等寿命的对象尽量在Minor GC时被干掉,最终在总体上减少Full GC的次数。

为什么不是1个Survivor空间?新生代一般采用复制算法进行垃圾收集。使用2个Survivor空间可以避免碎片

虚拟机会给每个对象定义一个对象年龄(Age)计数器,对象在Survivor区中每“熬过”一次GC,年龄就会+1。待到年龄到达一定岁数(默认是15岁),虚拟机就会将对象移动到老年代。

如果新生对象在Eden区无法分配空间时,此时发生Minor GC。发生MinorGC,对象会从Eden区进入Survivor区,如果Survivor区放不下从Eden区过来的对象时,此时会使用分配担保机制将对象直接移动到年老代。

  1. 第一次Young GC(Minor GC)后,Eden区还存活的对象复制到Surviver区的“TO”区,“FROM”区还存活的对象也复制到TO区。
  2. 再清空Eden区和From区,这样就等于“From”区完全是空的了。而“To”区也不会有内存碎片产生。
  3. 等到第二次Young GC时,“From”区和“To”区角色互换,很好地解决了内存碎片的问题。

在新生代中,每次垃圾收集时都发现有大批对象结束,只有少量存活,那就选用“复制算法”,只需要付出少量存活对象的复制成本就可以完成收集。

Major GC的触发条件

Major GC又称为Full GC。当老年代空间不够用的时候,虚拟机会使用“标记——清除”或者“标记——整理”算法清理出连续的内存空间,分配对象使用。

老年代

主要存放应用程序中生命周期长的内存对象。

老年代的对象比较稳定。所以MajorGC不会频繁执行。

在MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋升入老年代,导致空间不够时才触发。

当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

在老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

MajorGC采用标记-清除算法:

首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC 会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满了装不下的时候,就会抛出OOM (Out of Memory)异常。

永久代

指内存的永久保存区域。主要存放Class和Meta(元数据)的信息。

Class在被加载的时候被放入永久区域,它和存放实例的区域不同,GC不会在主程序运行期间对永久区域进行清理。

这就导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

Java8与元数据

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

元空间的本质和永久代类似。元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下, 元空间的大小仅受本地内存限制。

类的元数据放入native memory,字符串池和类的静态变量放入java堆中。这样可以加载的类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。

GC算法描述

标记-清除算法

1、标记阶段:首先通过根节点,标记所有从根节点开始的可达对象。未被标记的对象就是未被引用的垃圾对象。
2、清除阶段:清除所有未被标记的对象。

缺点:完成后存在较多的内存碎片。

复制算法(Copying)

  1. 将原有的内存空间分为两块,每次只使用一块。
  2. 在垃圾回收时,将正在使用的内存中存活对象复制到未被使用的内存块中,然后清除正在使用的内存块中的所有对象
  3. 交换两个内存的角色,完成垃圾回收

与标记-清除算法相比,复制算法是一种相对高效的回收方法。

缺点:总空间被减少了一半。

标记-整理算法

  1. 标记阶段:先通过根节点,标记所有从根节点开始的可达对象,未被标记的为垃圾对象
  2. 整理阶段:将所有的存活对象压缩到内存的一段,之后清理边界外所有的空间。