Sun JDK中可用的GC
Sun JDK根据运行的Java程序进行分析,认为程序中大部分对象的存活时间都是较短的,少部分对象是长期存活的。基于这个分析,Sun JDK将JVM堆划分为了新生代和旧生代,并基于新生代和旧生代中对象存活时间的特征提供了不同的GC实现,如图所示。
Java Hotspot Mode:
server 和 client两种模式,如果不配置,JVM会根据应用服务器硬件配置自动选择模式,server模式启动比较慢,但是运行期速度得到了优化,client启动比较快,但是运行期响应没有server模式的优化,适合于个人PC的服务开发和测试。
常用的垃圾收集器包括下面几个:
Serial:最基本,历史最悠久的收集器,单线程收集垃圾内存,在新生代采用复制算法,在老生代使用标记-整理算法
ParNew:Serial的多线程版本,主要用于新生代收集。与CMS收集器配合成为现在最常用的server收集器
Parallel Scavenge:也是一个并行收集器,使用与ParNew完全不同的收集策略,具体的差别还在研究中
CMS:Concurrent Mark Sweep收集器,大名鼎鼎,其目标是获取最短回收停顿时间,是server模式下最常用的收集器
G1:最新的收集器
ParallelGC采用了多线程并行管理和回收垃圾对象,提高了回收效率,提高了服务器的吞吐量,适合于多处理器的服务器。ConcMarkSweepGC采用的是并发方式来管理和回收垃圾对象,降低垃圾回收产生的响应暂停时间。这里说一下并发和并行的区别,并行指的是多个进程并行执行垃圾回收,那么可以很好的利用多处理器,而并发指的是应用程序不需要暂停可以和垃圾回收线程并发工作。串行GC适合小型应用和单处理器系统(无需多线程交互,效率比较高),后两者适合大型系统。
使用方式就是在参数配置中增加-XX:+UseParallelGC等方式来设置。
对于这部分的配置在网上有很多的实例可以参考,不过最终采用哪一种GC还是要根据具体的情况来分析和选择。
Garbage First
除了以上这些已有的GC外,为了能够做到控制某个时间片内GC所能占用的最大暂停时间,例如在100秒内最多允许GC导致的应用暂停时间为1秒,以满足对响应时间有很高要求的应用,Sun JDK 6 Update 14以上版本及 JDK 7中增加了一种称为Garbage First的GC。
Garbage First简称G1,它的目标是要做到尽量减少GC所导致的应用暂停的时间,同时保持JVM堆空间的利用率。
G1和CMS GC非常类似,只是G1将JVM Heap划分为了更小粒度的regions,并在回收时进行了估算。回收能够带来最大内存空间的regions,从而缩短了每次回收所须消耗的时间。
关于G1原理详细讲解可见http://book.51cto.com/art/201011/235595.htm
关于各个GC原理的详尽分析可见http://book.51cto.com/art/201011/235574.htm BlueDavy(林昊) 分布式Java应用
Real-Time JDK
为了满足实时领域系统使用Java的需求,Java推出了Real-Time版的规范(JSR-001,更新的版本为JSR-282)。Real-Time版的JDK对Java做出了很多的改进,例如强大的线程调度机制、异步的事件处理机制、更为精准的时间刻度等,在此最为关心的是其在Java内存管理方面的加强。
GC无疑是Java进入实时领域的一个很大障碍,毕竟无论GC怎么改进,它肯定会造成应用暂停的现象,而且会在运行时突然造成暂停。这对于实时系统来说是不可接受的,因此Real-Time版的JDK在此方面做了多方面的改进。
1. 新的内存管理机制
提供了两种内存区域:Immortal内存区域和Scoped内存区域。
Immortal内存区域用于保留永久的对象,这些对象仅在应用结束运行时才会释放内存,这对于支持需要缓存的系统而言非常重要。
Scoped内存区域用于保留临时的对象,位于scope中的对象在scope退出时,这些对象所占用的内存会被直接回收,有点类似于栈上分配。
Immortal内存区域和Scoped内存区域均不受GC管理,因此基于这两个内存区域来编写的应用完全不用担心GC会造成暂停的现象。
2. 允许Java应用直接访问物理内存
在保证安全的情况下,Real-Time JDK允许Java应用直接访问物理内存,而非像以前的Java程序,需要通过native code才能访问。能够访问物理内存,也就意味着可以直接将对象放入物理内存,而非JVM heap中。
小结
表1 client、server模式的默认GC
表2 Sun Jdk GC组合方式
为了避免开发人员选择哪种GC而头疼,Sun JDK还提供了两种简单的方式来帮助选择GC。
1. 吞吐量优先
吞吐量是指GC所耗费的时间占应用运行总时间的百分比,例如应用总共运行了100分钟,其中GC执行占用了1分钟,那么吞吐量就是99%,JVM默认的指标是99%。
吞吐量优先的策略即为以吞吐量为指标,由JVM自行选择相应的GC策略及控制New Generation、Old Generation内存的大小,可通过在JVM参数中指定-XX:GCTimeRatio=n来使用此策略。
2. 暂停时间优先
暂停时间是指每次GC造成的应用的停顿时间,默认不启用这个策略。
暂停时间优先的策略即为以暂停时间为指标,由JVM自行选择相应的GC策略及控制New Generation、Old Generation内存的大小,来尽量保证每次GC造成的应用停顿时间都在指定的数值范围内完成,可通过在JVM参数中指定-XX:MaxGCPauseMillis=n来使用此策略。
当以上两参数都指定的情况下,首先满足暂停时间优先策略,再满足吞吐量优先策略,这两个策略要通过收集运行信息来做动态调整,实现难度较高,目前还很难做到精确的控制,并且动态地调整可能会给应用带来影响,以及大部分应用的需求其实是一定时间范围内吞吐量是多少或暂停时间最多是多少,这在这两种策略中无法配置。
写在最后:
性能优化在应用方面可以有很多手段,包括Cache,多线程,各种算法等等。通常情况下是不建议在没有任何统计和分析的情况下去手动配置JVM的参数来调整性能,因为在JVM 5以上已经作了根据机器和OS的情况自动配置合适参数的算法,基本能够满足大部分的情况,当然这种自动适配只是一种通用的方式,如果说真的要达到最优,那么还是需要根据实际的使用情况来手动的配置各种参数设置,提高性能。
JVM能够对性能产生影响的最大部分就是对于内存的管理。从jdk 1.5以后内存管理和分配有了很多的改善和提高。