GC参数
串行收集器
– 最古老,最稳定
– 效率高
– 可能会产生较长的停顿
– 新生代、老年代使用串行回收
– 新生代复制算法
– 老年代标记-压缩
– -XX:+UseSerialGC
并行收集器
– 新生代复制算法,老年代标记-压缩
– -XX:+UseParNewGC
– Serial收集器新生代的并行版本
– 只会影响新生代
– 可以和CMS算法一起使用
– -XX:+UseParallelGC
– ParNew是这个的升级版
– 使用Parallel收集器+ 老年代串行
– 更加关注吞吐量
– -XX:+UseParallelOldGC
– 使用Parallel收集器+ 并行老年代
– -XX:MaxGCPauseMills
– 最大停顿时间,单位毫秒
– GC尽力保证回收时间不超过设定值
– -XX:GCTimeRatio
– 0-100的取值范围
– 垃圾收集时间占总时间的比
– 默认99,即最大允许1%时间做GC
– 和-XX:MaxGCPauseMills冲突,因为停顿时间和吞吐量不可能同时调优
– 多线程,需要多核支持
– -XX:ParallelGCThreads 限制线程数量
– 建议根据PC的CPU核数来限制线程数量
CMS收集器
– Concurrent Mark Sweep 并发标记清除
– 标记-清除算法
– 并发阶段会降低吞吐量
– 老年代收集器(新生代使用ParNew)
– -XX:+UseConcMarkSweepGC
– -XX:+ UseCMSCompactAtFullCollection
– Full GC后,进行一次碎片整理
– 整理过程是独占的,会引起停顿时间变长
– -XX:ParallelCMSThreads
– 设定CMS的线程数量
– -XX:CMSFullGCsBeforeCompaction
– 设置进行几次Full GC后,进行一次碎片整理
– -XX:+CMSClassUnloadingEnabled
– 允许对类元数据进行回收
– -XX:CMSInitiatingPermOccupancyFraction
– 当永久区占用率达到这一百分比时,启动CMS回收
– -XX:+UseCMSInitiatingOccupancyOnly
– 表示只在到达阀值的时候,才进行CMS回收
– 特点
– 尽可能降低停顿
– 会影响系统整体吞吐量和性能
– 比如,在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半
– 清理不彻底
– 因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理
– 因为和用户线程一起运行,不能在空间快满时再清理
– -XX:CMSInitiatingOccupancyFraction设置触发GC的阈值
– 如果不幸内存预留空间不够,就会引起concurrent mode failure
33.348: [Full GC 33.348: [CMS33.357: [CMS-concurrent-sweep: 0.035/0.036 secs] [Times: user=0.11 sys=0.03, real=0.03 secs]
(concurrent mode failure): 47066K->39901K(49152K), 0.3896802 secs] 60771K->39901K(63936K), [CMS Perm : 22529K->22529K(32768K)], 0.3897989 secs] [Times: user=0.39 sys=0.00, real=0.39 secs]
使用串行收集器作为后备
– CMS运行过程
– 初始标记
– 根可以直接关联到的对象
– 速度快
– 并发标记(和用户线程一起)
– 主要标记过程,标记全部对象
– 重新标记
– 由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正
– 并发清除(和用户线程一起)
– 基于标记结果,直接清理对象
Trace跟踪参数
-XX:+PrintGC
– 可以打印GC的简要信息
– 示例
[GC 4790K->374K(15872K), 0.0001606 secs]
[GC 4790K->374K(15872K), 0.0001474 secs]
-verbose:gc
功能和-XX:+printGC一样,但是这个是稳定版,-XX:+PrintGC是非稳定版本,可能在未通知的情况下删除。
-XX:+PrintGCDetails
– 打印GC详细信息
– 示例
[GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
系统运行完成前的快照:堆信息
-XX:+PrintGCTimeStamps
– 打印CG发生的时间戳
– 单独使用无效,一般和-XX:+PrintGC、-XX:+PrintGCDetails等参数搭配使用
-Xloggc:log/gc.log
– 指定GC log的位置,以文件输出
– 帮助开发人员分析问题
-XX:+PrintHeapAtGC
– 每次一次GC后,都打印堆信息
-XX:+TraceClassLoading
– 监控类的加载
[Loaded java.lang.Object from shared objects file]
[Loaded java.io.Serializable from shared objects file]
[Loaded java.lang.Comparable from shared objects file]
[Loaded java.lang.CharSequence from shared objects file]
[Loaded java.lang.String from shared objects file]
[Loaded java.lang.reflect.GenericDeclaration from shared objects file]
[Loaded java.lang.reflect.Type from shared objects file]
-XX:+PrintClassHistogram
– 按下Ctrl+Break后,打印类的信息
– 分别显示:序号、实例数量、总大小、类型
– 示例
num #instances #bytes class name
----------------------------------------------
1: 890617 470266000 [B
2: 890643 21375432 java.util.HashMap$Node
3: 890608 14249728 java.lang.Long
4: 13 8389712 [Ljava.util.HashMap$Node;
5: 2062 371680 [C
6: 463 41904 java.lang.Class
堆的分配参数
-Xmx -Xms
– 指定最大堆和最小堆
– Java会尽可能维持在最小堆
– 当堆使用的内存超过了最小值并且小于最大值,JVM会自动扩展堆内存,但不会扩展超过最大值。
-Xmn
– 设置新生代大小,老年代大小则默认为堆内存减去新生代的大小
-XX:NewRatio=4
– 和-Xmn会有冲突,以-Xmn为准
– 新生代(eden+2*s)和老年代(不包含永久区)的比值
– 4 表示 新生代:老年代=1:4,即年轻代占堆的1/5
– 默认值为2
-XX:SurvivorRatio=8
– 设置两个Survivor区和eden的比
– 8表示 两个Survivor :eden=2:8,即一个Survivor占年轻代的1/10
– 默认值为8
-XX:+HeapDumpOnOutOfMemoryError
– OOM时导出堆到文件
-XX:HeapDumpPath=d:/a.dump
– 导出OOM的路径
-XX:OnOutOfMemoryError
– 在OOM时,执行一个脚本
-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p
示例
示例1、
堆的最大最小都设为20m,新生代大小为1m
程序创建10个大小为10M的对象
运行结果:没有触发GC,因为新生代空间太小,对象直接分配在老年代空间
示例2、
堆的最大最小都设为20m,新生代大小为15m
程序创建10个大小为10M的对象
运行结果:没有触发GC,对象全部分配在eden空间
示例3、
堆的最大最小都设为20m,新生代大小为7m
程序创建10个大小为10M的对象
运行结果:进行了2次新生代GC,一共回收7M 剩余3M,S0 s1 太小无法周转,GC之后对象保存在老年代
示例4、
堆的最大最小都设为20m,新生代大小为7m,eden区和幸存区(from+to)各占新生代的一半大小
程序创建10个大小为10M的对象
运行结果:一共回收7M 剩余3M,老年代没有使用
总结
– 根据实际事情调整新生代和幸存代的大小
– 官方推荐新生代占堆的3/8
– 幸存代占新生代的1/10
– 在OOM时,记得Dump出堆,确保可以排查现场问题
永久区分配参数
-XX:PermSize -XX:MaxPermSize
– 设置永久区的初始空间和最大空间
– 他们表示,一个系统可以容纳多少个类型
– 如果堆空间没有用完也抛出了OOM,有可能是永久区导致的
– 默认值64m,64位VM会扩大30%
栈的分配参数
-Xss
– 通常只有几百K
– 决定了函数调用的深度
– 每个线程都有独立的栈空间
– 局部变量、参数 分配在栈上
– 计算可创建线程数
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory 指的是一个进程的最大内存
JVMMemory JVM内存
ReservedOsMemory 保留的操作系统内存
ThreadStackSize 线程栈的大小
– 默认值
· Linux/ARM (32-bit): 320 KB
· Linux/i386 (32-bit): 320 KB
· Linux/x64 (64-bit): 1024 KB
· OS X (64-bit): 1024 KB
· Oracle Solaris/i386 (32-bit): 320 KB
· Oracle Solaris/x64 (64-bit): 1024 KB
总结
性能的根本在应用,GC参数属于微调,设置不合理,会影响性能,产生大的延时
升级JDK可能会带来额外的性能提升!