Eden、Survivor 、年老代和永久代的关系

在JVM的堆内存中,通常将堆内存分为新生代和老年代两个部分,其中新生代包括Eden区和两个Survivor区(通常是From和To),老年代包括年老代和永久代(或者是元数据区)。

新生代是用来存放新创建的对象的,其中Eden区是对象的出生地,大部分的新对象都会被分配到Eden区,当Eden区满了之后,存活的对象会被复制到Survivor区中,而其中一块Survivor区是空的,这时候可以将非存活对象清除掉,存活的对象会被复制到另一块Survivor区中,这样交替使用两块Survivor区,可以避免频繁地进行全局垃圾回收。

当Survivor区无法容纳存活对象时,这些对象就会被转移到年老代中,年老代主要用来存放存活时间较长的对象,这些对象可以在多次垃圾回收中保持不变。

而永久代(或元数据区)则是用来存放JVM运行时需要的类信息、常量池等元数据,这部分内存通常被限制在64MB左右,并且永久代的大小不会随着应用程序的运行而改变。在Java 8及之后的版本中,永久代已经被移除,取而代之的是元空间(Metaspace)。

总体来说,Eden区和Survivor区主要是用于新生代对象的分配和垃圾回收,而年老代则是用于存放生命周期较长的对象,永久代(或元空间)则是用于存放JVM运行时需要的元数据。

在JVM中,Java Heap可以被分为几个区域,包括年轻代、年老代和永久代。其中,年轻代包括一个Eden空间和两个Survivor空间(一般称为S0和S1),年老代和永久代是分别存放长时间存活的对象和Java类的区域。

在年轻代中,大部分对象都是短时间存活的,因此采用了分代收集的思想。当Eden区域满时,会触发Minor GC,回收Eden区域和S0/S1区域中的垃圾对象。存活的对象会被复制到另一个空间中,比如从Eden区域复制到S0区域。每个Survivor区域的大小一般为Eden区域大小的一半,用于存放Eden区域和另一个Survivor区域中的存活对象。因此,当一次Minor GC后,存活的对象会被复制到另一个Survivor区域中。

当一个对象经过多次复制后,如果仍然存活,它就会被移动到年老代。年老代的大小一般比年轻代大得多,因为它要存放长时间存活的对象。当年老代空间不足时,会触发Major GC或Full GC,回收整个堆内存中的垃圾对象。

永久代用于存放Java类的信息,比如类的名称、方法的名称和签名、字段的名称和类型等。由于这些信息在应用程序运行期间基本上不会发生变化,因此永久代的垃圾回收比较简单,可以采用“标记-清除”算法。然而,永久代的大小是固定的,如果应用程序加载了过多的类,就会导致永久代空间不足,出现“PermGen space”错误。