在jvm中堆空间划分为三个代:年轻代(Young Generation)、年老代(Old Generation)和永久代(Permanent Generation)。年轻代和年老代是存储动态产生的对象。永久带主要是存储的是java的类信息,包括解析得到的方法、属性、字段等等。永久带基本不参与垃圾回收。我们这里讨论的垃圾回收主要是针对年轻代和年老代。
具体如下图:
年轻代又分成3个部分,一个eden区和两个相同的survior区。刚开始创建的对象都是放置在eden区的。分成这样3个部分,主要是为了生命周期短的对象尽量留在年轻带。当eden区申请不到空间的时候,进行minorGC,把存活的对象拷贝到survior。年老代主要存放生命周期比较长的对象,比如缓存对象。具体jvm内存回收过程描述如下(可以结合上图):
1、对象在Eden区完成内存分配
2、当Eden区满了,再创建对象,会因为申请不到空间,触发minorGC,进行young(eden+1survivor)区的垃圾回收
3、minorGC时,Eden不能被回收的对象被放入到空的survivor(Eden肯定会被清空),另一个survivor里不能被GC回收的对象也会被放入这个survivor,始终保证一个survivor是空的
4、当做第3步的时候,如果发现survivor满了,则这些对象被copy到old区,或者survivor并没有满,但是有些对象已经足够Old,也被放入Old区 XX:MaxTenuringThreshold
5、当Old区被放满的之后,进行fullGC
在知道垃圾回收机制以后,大家可以在对jvm中堆的各个参数进行优化设置,来提高性能。
下面介绍JVM中各种内存大小的说明
1)Heap Size
-Xmx ---max-heap-size,限制了年轻代和年老代的可分配最大值;
-Xms ---初始化分配的heap-size
生产环境中ms一般设置成跟mx相等,因为若ms不等于mx那么在某些场景下JVM可能需要对Heap Size进行频繁的扩展和收缩,增加处理时间;
2)New/Young Generation Size
-Xmn ---最大年轻代大小,即上图中的Eden+S0+S1+Virtual
-XX:NewSize ---初始化年轻代大小,即上图中的Eden+S0+S1,在只设置了-Xmn不设置-XX:NewSize的情况下,NewSize等于mn。
生产环境中一般只需设置-Xmn或者设置mn和NewSize相等,理由和HeapSize的设置一样,避免容量震荡消耗资源;
3)Old Generation Size (Tenured)
-XX:NewRatio --- Old Size/New Size,通过年老代和年轻代的比例和Heap Size就可以算出年老代的大小。一般默认为8,若Heap Size为1024m,则 NewSize=HeapSize/(NewRatio+1)=114m,OldSize=HeapSize-NewSize=910m;
注意:-Xmn的优先级比-XX:NewRatio高,若-Xmn已指定,则OldSize=HeapSize-NewSize,无需再按比例计算。生产环境中一般只需指定-Xmn就足够了。
4)Eden和S0、S1
-XX:SurvivorRatio --- Eden/S0,即 Eden区和S0的比例,默认为8,若NewSize为114m,则S0=NewSize/(SurvivorRatio+2)=11.4m;
S0==S1,S0、S1的职能是一模一样的,又叫做From space和To space,在每一次minor gc后角色会交换。
5)Permanent Generation Size
-XX:MaxPermSize ---最大持久代大小,默认为64m;
-XX:PermSize ---初始化持久代大小,默认为16m;
生产环境中一般设置MaxPermSize和PermSize相等,理由和HeapSize的设置一样,避免容量震荡消耗资源;
当应用引用的类比较多或者应用了一些动态类生产技术时应该加大该区的值,一般256m对服务器程序都很足够了。
6)Thread Stack Size
-Xss ---线程堆栈大小,一般用于存放方法入口参数和返回值,以及原子类型的本地变量(即方法内部变量);
一般可设置为128k.
下面介绍GC运行原理
在讲述GC过程前我先解释一下JVM的两个控制参数:
-XX:TargetSurvivorRatio --- Survivor Space最大使用率,若存放对象的总大小超过该值,将引起对象向Old区迁移;
-XX:MaxTenuringThreshold --- Young区对象的最大任期阀值,即可经历minor gc的次数,超过该次数的对象直接迁移到Old区;
实际的TenuringThreshold由JVM通过Survivor Space的占用率和TargetSurvivorRatio动态计算,详情请查看参考资料。
1)Heap在初始状态
2)在Eden存放新对象
3)Eden空间不足分配新对象,进行第一次minor gc
4) Eden区再次被写满,进行第二次minor gc
5)Eden再次被写满,进行第3次minor gc
第3次gc,发生了对象从from space提升到old区的迁移,然后也发生了from space到to space的copy
以下是Survivor space空间不足但对象的minor gc次数未到达MaxTenuringThreshold时的gc情况: