本文是基于Oracle的Hotspot JVM 1.6版本的分析。
先来看一下 HotSpot的内存结构:
备注:在HotSpot中本地方法栈和JVM方法栈是同一个,因此也可以用-Xss控制。
经IBM研究,通常运行的程序有80%--98%的对象是临时对象,因此Hotspot对JVM堆采用了分代的方式来管理,以提升GC的效率。
下面我们来看一张堆内存逻辑划分图:
分为New Generation或Young Generation(包含Eden Space、S0、S1)、Old Generation、Permanent Generation。
备注:New Generation中大部分为临时对象,因此采用了复制算法进行实现。通常将对New Generation进行的回收称为Minor GC;对Old Generation进行的回收称为Major GC, 但由于Major GC除并发GC外均需对整个堆以及Permanent Generation进行扫描和回收,因此又称为Full GC。
其中Permanent Generation主要存储了class的信息,详细可参考:
http://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation
不过在jdk7中此区域要被移除了。
下面紧接着的一个问题就是何时进行Minor GC、何时进行Major GC?
结合上图我们来简单的描述下回收的过程:
- 对象在Eden Space完成内存分配
- 当Eden Space满了,再创建对象,会因为申请不到空间,触发Minor GC,进行New(Eden + S0 或 Eden S1) Generation进行垃圾回收
- Minor GC时,Eden Space不能被回收的对象被放入到空的Survivor(S0或S1,Eden肯定会被清空),另一个Survivor里不能被GC回收的对象也会被放入这个Survivor,始终保证一个Survivor是空的
- 在Step3时,如果发现Survivor区满了,则这些对象被copy到old区,或者Survivor并没有满,但是有些对象已经足够Old,也被放入Old Space。
- 当Old Space被放满之后,进行Full GC
但这个具体还要看JVM是采用的哪种GC方案!
New Generation的GC有以下三种:
对于上述三种GC方案均是在Eden Space分配不下时,触发GC.
Old Generation的GC有以下四种:
对于Serial MSC, Parallel MSC, Parallel Compacting而言触发机制为
- Old Generation空间不足
- Permanent Generation空间不足
- Minor GC时的悲观策略
- Minor GC后在Eden上分配内存仍然失败
- 执行Heap Dump时
- 外部调用System.gc,可通过-XX:+DisableExplicitGC来禁止
- 当Old Generation空间使用到一定比率时触发;HopSpot V1.6中默认是92%,可通过PrintCMSInitiationStatistics(此参数在V1.5中不能用)来查看这个值到底是多少;可通过CMSInitiatingOccupancyFaction来强制指定,默认值并不是复制在这个值上,是根据如下公式计算出来的:((100 -MinHeapFreeRatio) +(double)(CMSTriggerRatio* MinHeapFreeRatio) / 100.0)/ 100.0;MinHeapFreeRatio默认值:40 CMSTriggerRatio默认值:80
- 当Permanent Generation采用CMS收集且空间使用到一定比率触发;Permanent Generation采用CMS收集需设置:-XX:+CMSClassUnloadingEnabled Hotspot V1.6中默认为92%;可通过CMSInitiatingPermOccupancyFraction来强制指定,同样,它是根据如下公式计算出来的:((100 -MinHeapFreeRatio) +(double)(CMSTriggerPermRatio* MinHeapFreeRatio) / 100.0)/ 100.0;MinHeapFreeRatio默认值:40 CMSTriggerPermRatio默认值:80
- Hotspot根据成本计算决定是否需要执行CMS GC;可通过-XX:+UseCmsInitiatingOccupancyOnly来去掉这个动态执行的策略。
- 外部调用System.gc,且设置了ExplicitGCIInvokesConcurrent;需要注意,在hotspot 6中,在这种情况下如果应用同时使用了NIO,可能会出现bug。
备注:关于究竟采用何种GC方案,在后续JVM调优的章节中再详细阐述。