调优指标
运行时间=程序的运行时间+内存回收时间
- 吞吐量:运行用户代码的时间占总运行时间的比例
- 暂停时间:执行垃圾回收时,程序的线程被暂停的时间
- 内存占用:java堆区占用的内存大小
这三者不能同时满足,一款垃圾回收器最多同时满足其中两种
- 吞吐量优先,则单位时间内,STW的时间最短
- 暂停时间优先,则尽可能的让单次STW时间最短
垃圾收集器分类
- 按线程数分,可以分为串行垃圾回收器和并行垃圾回收器
- 串行垃圾回收:同一个时间段内允许有一个cpu用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集工作结束
- 并行垃圾回收:并行可以拥有多个cpu同时执行垃圾回收,因此提升了应用的吞吐量,不过并行回收仍然和串行回收一样采用独占式,使用"STW",在回收的时候,需要暂停所有的线程
- 按工作模式分,分为并发式垃圾回收器和独占式回收器
- 并发:与应用程序交替运行,以尽量减少应用程序的停顿时间
- 独占:STW,一旦运行,停止所有用户线程,知道垃圾回收完全结束
- 按工作的内存区间,又可分为年轻代垃圾回收器和老年代垃圾回收器
- 年轻代:效率高,采用复制算法,但对内存的占用控制不精确,容易造成内存溢出
- 老年代:效率低,执行的时候会STW,但一般启动次数较少
HotSpot虚拟机中的垃圾收集器
- 串行:Serial, Serial Old
- 并行:ParNew, Parallel scavenge, Parallel Old
- 并发:CMS, G1
- 新生代:Serial, ParNew, Parallel scavenge
- 老年代:Serial Old, Parallel Old, CMS
- 堆:G1
1. Serial
历史最悠久的单线程收集器
特点:简单高效,采用复制算法,STW
应用:适用于Client模式下的虚拟机
2. Serial Old
Serial的老年代版本,采用标记-整理算法
应用:主要用户Client模式下,也可用于Server模式下
3. ParNew
Serial的多线程版本
特点:多线程,其他与Serial基本一致,默认开启CPU数量相同的线程
应用:ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器,它是除了Serial收集器外,唯一一个能与CMS收集器配合工作的
4. Parallel Scavenge
标记-复制,多线程
特点:它的关注点与其他的收集器不同,CMS等收集器的关注点是尽可能的缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量
吞吐量就是处理器用于远程用户代码的时间与处理器总消耗时间的比值
5. Parallel Old
Parallel Scavenge的老年代版本
特点:支持多线程并发,标记-整理
应用:注重吞吐量或者处理器资源稀缺的场合,优先考虑Parallel Scavenge和Parallel Old组合
6. CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器
运行过程:初始标记->并发标记->重新标记->并发清除
特点:并发手机、低停顿
缺点:
- 对处理器资源敏感
- 无法处理浮动垃圾
- 会产生内存碎片
扩展点:三色标记法
7. G1
G1的适用场景:面向服务端的应用,针对具有大内存、多处理器的机器(在普通大小的堆中表现并不出色),最主要的应用是需要低GC延迟并具有大堆的应用程序提供解决方案(G1通过每次只清理一部分而不是全部Region的增量式清理来保证每次GC停顿时间不会过长),在堆大小约6G或更大时,可预测的暂停时间可以低于0.5s
以下情况,G1可能比CMS好
- 超过50%的java堆呗活动数据占用
- 对象分配频率或年代提升频率变化很大
- GC停顿时间过长(大于0.5至1s)
从经验上来说,整体而言:
- 小内存应用上,CMS大概率会优于G1
- 大内存应用上,G1则可能更胜一筹,这个临界点大概在6~8g之间
各收集器的适用场景
- 如果需要最小化的使用内存和并行开销,使用Serial Old + Serial
- 如果需要最大化应用程序的吞吐量,使用Parallel Old + Parallel
- 如果需要最小化GC的终端或停顿时间,使用CMS + ParNew
在Java7update release 4(通常称为Java7u4或者JDK7u4)中,-XX:+UseParallel 01dGC被做成缺省的垃圾收集器以及并行垃圾收集器的标准操作模式。从Java7u4开始,指定-XX:+UseParallelGc 也会激活-XX:+UseParallel 0ldGC,同样地,-XX:+UseParallel 01dGC也会激活-Xx:+UseParallelGo
JVM调优
1. 优先原则
优先架构调优和代码调优,JVM优化是不得已的手段,大多数java程序不需要进行JVM优化
2. 堆设置
一般-Xms
和 -Xmx
设置为相同的值,避免运行时要不断的扩展JVM内存,建议扩大至3-4倍fullgc后的老年代空间占用
3. 年轻代设置
设置参数-Xmn
, 一般设置为1-1.5倍fullgc之后的老年代空间
新生代设置过小时:
- minor gc次数频繁
- 可能导致minor gc 对象直接进入老年代,当老年代内存不足时,会出发fullgc
新生代设置过大时:
- 老年代变小,可能导致fullgc频繁
- minor gc执行时间大幅增加
4. 老年代的设置
- 老年代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数
- 如果堆设置偏小,可能会造成内存碎片、高回收频率以及应用暂停
- 如果堆设置偏大,则需要较长的收集时间
吞吐量优先的应用 一般吞吐量优先的应用都有一个较大的年轻代和一个较小的老年代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽可能存放长期存活对象
5. 方法区设置
- jdk1.7:永久代:参数-XX:PermSize和-XX:MaxPermSize
- jdk1.8: 元空间:参数 -XX:MetaspaceSize和-XX:MaxMetaspaceSize
- 一般设置为相同的值,避免不必要的扩展,建议扩大至1.2-1.5倍fullgc后的永久代空间