年轻代&老年代( Young Generation & Old Generation)
- 年轻代:年轻代其实分为两部分,分别是1个Eden区和2个Survivor区(分别叫from和to),默认比例是8:1,一般情况下,新创建的对象都会被分配到Eden区,(除非一些特别大的对象会直接放到老年代),当Eden没有足够的空间的时候,就会触发jvm发起一次Minor GC,如果对象经过一次Minor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空间当中,对象在Survivor区中每熬过一次Minor GC,年龄就会增加一岁,当它的年龄增加到一定程度(15岁)时,就会被移到老年代中,当然晋升老年代的年龄是可以设置的。
- 老年代:当年轻带随着不断地Minor GC ,from survivor中的对象会不断成长,当from survivor中的对象成长大15岁的时候(对象晋升老年代的年龄阔值,可以通过选项-XX:MaxTenuringThreshold 来设置),就会进入老年代,随着Minor GC的持续进行,老年代中对象也会持续增长,最终老年代的空间也会不够用,此时就会执行老年代的GC–>Major GC(Full GC)。Major GC使用的算法是标记清除算法或者标记-压缩算法。
- 永久区( PermGen Space )
PermGen Space 的全称是 Permanent Generation Space,是指内存的永久保存区域。这一部分 用于存放 Class 和 Meta 的信息, Class 在被加载的时候被放入 PermGen Space 区域。它和存放常 量的堆区域不同, GC 不会在主程序运行期对 PermGen Space 进行清理,所以如果应用程序会加 载很多类的话,就很可能出现 PermGen Space 错误。这种错误常见在 WEB 服务器对 JSP 进行 预先编译的时候,如果你的 Web App 使用了大量的第二方 Jar 包,并且其大小超过了 NM 默认 的大小,那么就会产生此错误信息了。
在整个 JDK7 的更新过程当中l,永久区还是存在并被使用的,只不过己经开始从它内部移 出数据了,大致一共移出了三类数据:
- 例如&APPLID2这样的符号(Symbols)被移到了本地堆区:
- 内部字符串被移到了 Java 堆区;
- 类静态属性被移到了 Java 堆区。
如果你使用的是 JDK7,而 GC 使用的是 GIGC,那么永久区只有在 Full GC 阶段才会被回 收。 GI 只会在永久区满了后才调用 Full GC 事件,或者在应用程序的生产速度比 GI 的垃圾回 收速度快时调用。
JDK8 HotSpot VM 开始使用本地化的内存空间来存放类的元数据,这个空间叫作元空间 ( Metaspace)。这样的修改意味着 Java.lang.OutOfMemoryError: PermGen 的空间问题将不复存 在,井且不再需要调整和监控这个内存空间,也就是说,在 JDK8 完成了对永久区的移除
针对不同年龄段的对象分配原则如下所示。
- 对象优先分配在 Eden 区,如果 Eden 区没有足够的空间时,虚拟机执行一次 MinorGC。
- 大对象直接进入老年代(大对象是指需要大量连续内存空间的对象) , Gl GC 针对大对 象有自己的处理方法, 请看第 4 章。这样做的目的是避免在 Eden 区和两个 Survivor 区之间发生 大量的内存拷贝(年轻代采用复制算法收集内存〉。
- 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过 了 1 次 Minor GC 会进入 Survivor 区,之后每经过一次 Minor GC,则对象的年龄加 l ,直到达 到阀值对象进入老年区。
- 动态判断对象的年龄。如果 Survivor 区中相同年龄的所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
- 空间分配担保。每次进行 MinorGC 时, JVM 会计算 Survivor 区移至老年区的对象的平 均大小,如果这个值大于老年区的剩余值大小则进行一次 Full GC,如果小于则进入检查 HandlePromotionFailure 逻辑。判断这个逻辑,如果是 True 则只进行 Minor GC,如果是 False 则进行 Full GC,具体逻辑实现如图 1-20 所示。