深入浅出 JVM 之调优 - JVM 调优
文章目录
!版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。
系统环境:
- JDK 1.8
深入浅出 JVM 系列文章
- 01.深入浅出 JVM 之 Java 虚拟机
- 02.深入浅出 JVM 之 Class 字节码文件
- 03.深入浅出 JVM 之字节码-常量池常量分析
- 04.深入浅出 JVM 之字节码-指令集简介
- 05.深入浅出 JVM 之类加载-类加载流程
- 06.深入浅出 JVM 之类加载-类加载器
- 07.深入浅出 JVM 之运行时数据区
- 08.深入浅出 JVM 之运行时数据区-堆
- 08.深入浅出 JVM 之运行时数据区-方法区
- 09.深入浅出 JVM 之运行时数据区-虚拟机栈
- 10.深入浅出 JVM 之运行时数据区-程序计数器
- 11.深入浅出 JVM 之垃圾回收-垃圾回收概述与算法
- ......
一、什么是 JVM 调优
二、JVM 调优考虑调什么
调优需要调整的两个方面
- 调整内存
- 调整线程
内存需要调整什么
- JVM 需要的内存总大小
- 各块内存分配,新生代、老年代、存活区
- 选择合适的垃圾回收算法,控制 GC 停顿的次数和时间
- 解决内存泄漏问题,辅助代码进行优化
- 内存热点,检查哪些对象在系统中的数量最多,辅助代码进行优化
线程需要调整什么
- 死锁检查,辅助代码进行优化
- Dump 线程详细信息,查看线程内部运行情况,查找竞争线程,辅助代码进行优化
- CPU 热点,检查系统哪些方法占用了大量 CPU 时间,辅助代码进行优化
三、JVM 如何进行调优
- 监控 JVM 的状态,主要是内存、线程、代码、I/O部分。
- 分析结果,判断是否需要优化。
- 调整使用的垃圾回收算法,并优化应用程序代码。
- 不断的重复监控,分析和调整,直至找到优化的平衡点。
四、JVM 调优的目标
- GC 的时间足够的小
- GC 的次数足够的少
- 将转移到老年代中的对象数量降低到最小
- 减少 Full GC 的执行时间
- 发生 Full GC 的时间间隔足够长
五、JVM 调优常见的策略
- 减少创建对象的数量
- 减少使用全局变量和大对象
- 调整新生代、老年代的大小到合适的位置
- 选择合适的垃圾回收器,并设置合理的参数
六、JVM 调优的思考
- 多数的 Java 应用不需要在服务器上进行 GC 优化
- 多数导致 GC 问题的 Java 应用,都不是因为参数设置错误,而是代码问题
- 在应用上线之前,先考虑将机器的 JVM 参数设置到最优 (最合适)
- JVM 优化是到最后不得已才采用的手段
- 在实际使用中,分析 JVM 情况优化代码比优化 JVM 本身要多得多
- 比如以下情况通常不用优化:
- 1 Minor GC 执行时间不到 50 ms;
- 2 Minor GC 执行不频繁,约 10 秒一次;
- 3 Full GC 执行时间不到 1 秒;
- 4 Full GC 执行的频率并不高,不低于 10 分钟一次;
七、JVM 调优经验
- 要想 GC 时间短,必须要一个更小的堆空间,而要保证 GC 的次数足够少,又必须保证一个更大的堆空间,这两个是有冲突的,只能取平衡。
- 针对 JVM 堆的设置,一般可以通过调整 -Xmx 和 -xms 俩个参数来配置堆的最大值和最小值,为了防止垃圾回收器在最小、最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值。
- 新生代和老年代将根据默认的比例 (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;
- 老年代的内存越来越大,并且每次 FullI GC 后年老代没有内存被释放;
- 老年代堆空间被占满的情况,这种情况解决方式就是根据垃圾回收前后对比,同时根据对象引用情况分析,辅助去查找泄漏点;
- 堆内存溢出情况,通常会抛出 java.lang.StackOverflowError 错误,导致这个错误很可能的原因就是递归调用的方法执行时没有退出,从而进行了深度递归导致;
--- END ---
!版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。