1. 前言
在上一篇文章中,介绍了JVM中垃圾回收的原理和算法。介绍了通过引用计数和对象可达性分析的算法来筛选出已经没有使用的对象,然后介绍了垃圾收集器中使用的三种收集算法:标记-清除、标记-整理、标记-复制算法。
介绍完原理,在这篇文章中,我们将介绍当前JVM中已经实现的垃圾收集器,以及与收集器主题相关的一些内容。
首先,我们将在上一篇文章中提到分代收集机制的基础上,介绍下现代商业JVM中普遍采用的分代回收策略。然后,按照内存分代划分的维度介绍下当前JVM中实现的收集器。最后,学习分析不同收集器的GC日志,然后结合日志分析,学习下不同情况下的对象分配策略。
2. 分代收集策略
我们知道,当对象被创建的时候,就会给对象分配一块内存空间,而一旦对象的生命周期结束,我们就需要回收这块内存空间。但是,在一个应用程序中,不同的对象存在的时间,或者说每个对象的生命周期都是不同的。
有些对象生命周期很短,比如Web应用程序中的request对象,它的生命周期和请求是对应的,当请求完成以后,该request对象就结束了它的职责,需要被收集器回收。有些对象的生命周期很长,比如一些全局的对象,可能会伴随整个应用程序的生命周期而存在。
在上图中,横轴表示对象的生命周期长短,竖轴表示对应生命周期下的对象数量。观察蓝色的区域,我们可以看到大部分的对象的生命周期都很短,而生命周期长的对象,它们的数量占据了小部分。
考虑到不同生命周期的对象的分布情况,为了合理的处理不同生命周期的对象回收问题。现代JVM的对不同生命周期的对象进行分类,对堆内存区域进行逻辑划分。按照对象的存活时间长短,将内存分为:年轻代、老年代和永久代(在Java8中去掉了永久代,以元数据空间代替)。这里我们主要关注年轻代和老年代的GC。
JVM提供了两个参数来控制JVM堆的大小:-XX:InitialHeapSize(-Xms)和-XX:MaxHeapSize(-Xmx)。JVM会根据应用程序使用内存的情况,动态扩展堆内存的大小,上图中的Virtual表示的区域,表示的就是可以扩展的内存空间。
比如,我们可以将JVM的堆内存设置为256M,最大512M的大小,那么可以这么设置:-Xms256m -Xmx512m。如果将Xms的值和Xmx的值设置为相同,那么JVM将不能动态扩展堆内存,它的初始堆内存和最大堆内存是相同的。