JVM - 调优
目录
调什么,如何调
内存方面
线程方面
如何调优
调优的目标,策略和冷思考
JVM调优的目标
常见调优策略
JVM调优冷思考
调优经验与内存泄漏分析
JVM调优经验
内存泄露
-
调什么,如何调
-
内存方面
- JVM需要的内存总大小
- 各块内存分配,新生代、老年代、存活区
- 选择合适的垃圾回收算法、控制GC停顿次数和时间
- 解决内存泄露的问题,辅助代码优化
- 内存热点:
- 检查哪些对象在系统中数量最大,辅助代码优化
-
线程方面
- 死锁检查,辅助代码优化
- Dump线程详细信息:
- 查看线程内部运行情况,查找竞争线程,辅助代码优化
- CPU热点:
- 检查系统哪些方法占用了大量CPU时间,辅助代码优化
-
如何调优
- 监控JVM的状态,主要是内存、线程、代码、I/O几个部分
- 分析结果,判断是否需要优化
- 调整:垃圾回收算法和内存分配;修改并优化代码
- 不断的重复监控、分析和调整,直至找到优化的平衡点
-
调优的目标,策略和冷思考
-
JVM调优的目标
- GC的时间足够的小
- GC的次数足够的少
- 将转移到老年代的对象数量降低到最小
- 减少Full GC的执行时间
- 发生Full GC的间隔足够的长
-
常见调优策略
- 减少创建对象的数量
- 减少使用全局变量和大对象
- 调整新生代、老年代的大小到最合适
- 选择合适的GC收集器,并设置合理的参数
-
JVM调优冷思考
- 多数的Java应用不需要在服务器上进行GC优化
- 多数导致GC问题的Java应用,都不是因为参数设置错误,而是代码问题
- 在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合)
- JVM优化是到最后不得已才采用的手段
- 在实际使用中,分析JVM情况优化代码比优化JVM本身要多得多
- 如下情况通常不用优化:
- Minor GC执行时间不到50ms
- Minor GC执行不频繁,约10秒一次
- Full GC执行时间不到1s
- Full GC执行频率不算频繁,不低于10分钟1次
-
调优经验与内存泄漏分析
-
JVM调优经验
- 要注意32位和64位的区别,通常32位的仅支持2-3g左右的内存
- 要注意client模式和Server模式的选择
- 要想GC时间小必须要一个更小的堆
- 而要保证GC次数足够少,又必须保证一个更大的堆,这两个是有冲突的,只能取其平衡
- 针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值
- 为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值
- 新生代和老年代将根据默认的比例(1 : 2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整
- 也可以通过-XX:newSize -XX:MaxNewSize来设置其绝对大小
- 同样,为了防止新生的堆收缩,通常会把-XX:newSize -XX:MaxNewSize设置为同样大小
- 合理规划新生代和老年代的大小
- 如果应用存在大量的临时对象,应该选择更大的新生代
- 如果存在相对较多的持久对象,老年代应该适当增大
- 在抉择时应该本着Full GC尽量少的原则,让老年代尽量缓存常用对象,JVM的默认比例1 : 2也是这个道理
- 通过观察应用一段时间,看其在峰值时老年代会占多少内存,在不影响Full GC的前提下,根据实际情况加大新生代,但应该给老年代至少预留1/3的增长空间
- 线程堆栈的设置:
- 每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太大了,一般256K就足用
- 在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程
-
内存泄露
- 内存泄露导致系统崩溃前的一些现象,比如:
- 每次垃圾回收的时间越来越长,Full GC时间也延长到好几秒
- Full GC的次数越来越多,最频繁时隔不到1分钟就进行一次Full GC
- 老年代的内存越来越大,并且每次Full GC后老年代没有内存被释放
- 老年代堆空间被占满的情况
- 这种情况的解决方式:
- 一般就是根据垃圾回收前后情况对比,同时根据对象引用情况分析,辅助去查找泄漏点
- 堆栈溢出的情况
- 通常抛出java.lang.StackOverflowError例外
- 一般就是递归调用没退出,或者循环调用造成