分类:
2008-09-11 14:32:02
作者简介:肖桦,江南白衣,开源项目SpringSide--春天的旁边发起者。
转自:
原本想把题目更简单的定为--《不要停》的,但还是自己YY一下就算了。
开发Server最大的障碍,就是JDK1.4版之前的的串行垃圾收集机制会引起长时间的服务暂停,明白原理后,想想那些用JDK1.3写Server的先辈,不得不后怕。
好在JDK1.4已开始支持多线程并行的后台垃圾收集算法,JDK5.0则优化了默认值的设置。
一、参考资料:
Tuning Garbage Collection with the 5.0 Virtual Machine 官方指南。
Hotspot memory management whitepaper 官方白皮书。
Java Tuning White Paper 官方文档。
FAQ about Garbage Collection in the Hotspot 官方FAQ,JVM1.4.2。
Java HotSpot 虚拟机中的垃圾收集 JavaOne2004上的中文ppt
A Collection of JVM Options JVM选项的超完整收集。
二、基本概念
1、堆(Heap)
JVM管理的内存叫堆。在32Bit操作系统上有1.5G-2G的限制,而64Bit的就没有。
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64但小于1G。
JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4但小于1G。
默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=指定。 默认空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=指定。
一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小,所以上面的两个参数没啥用。
2.基本收集算法
复制:将堆内分成两个相同空间,从根(ThreadLocal的对象,静态对象)开始访问每一个关联的活跃对象,将空间A的活跃对象全部复制到空间B,然后一次性回收整个空间A。因为只访问活跃对象,将所有活动对象复制走之后就清空整个空间,不用去访问死对象,所以遍历空间的成本较小,但需要巨大的复制成本和较多的内存。
标记清除(mark-sweep):收集器先从根开始访问所有活跃对象,标记为活跃对象。然后再遍历一次整个内存区域,把所有没有标记活跃的对象进行回收处理。该算法遍历整个空间的成本较大暂停时间随空间大小线性增大,而且整理后堆里的碎片很多。
标记整理(mark-sweep-compact):综合了上述两者的做法和优点,先标记活跃对象,然后将其合并成较大的内存块。
可见,没有免费的午餐,无论采用复制还是标记清除算法,自动的东西都要付出很大的性能代价。
3.分代
分代是Java垃圾收集的一大亮点,根据对象的生命周期长短,把堆分为3个代:Young,Old和Permanent,根据不同代的特点采用不同的收集算法,扬长避短也。
Young(Nursery),年轻代。研究表明大部分对象都是朝生暮死,随生随灭的。因此所有收集器都为年轻代选择了复制算法。 复制算法优点是只访问活跃对象,缺点是复制成本高。因为年轻代只有少量的对象能熬到垃圾收集,因此只需少量的复制成本。而且复制收集器只访问活跃对象,对那些占了最大比率的死对象视而不见,充分发挥了它遍历空间成本低的优点。
Young的默认值为4M,随堆内存增大,约为1/15,JVM会根据情况动态管理其大小变化。 -XX:NewRatio= 参数可以设置Young与Old的大小比例,-server时默认为1:2,但实际上young启动时远低于这个比率?如果信不过JVM,也可以用-Xmn硬性规定其大小,有文档推荐设为Heap总大小的1/4。
Young的大小非常非常重要,见“后面暂停时间优先收集器”的论述。
[1]