机器环境
指标 | 参数 |
---|---|
机器 | CPU 8核,内存8GB |
集群规模 | 单机 |
seqb_web版本 | 1.0 |
数据库 | 4核 16G |
JVM调优主要步骤
第一步:通过 jvisualvm.exe 监控内存泄露,跟踪垃圾回收,执行内存、CPU、线程分析或分析GC日志
将生成的日志文件,使用GC日志分析工具(gceasy.io/) 进行分析,然后下载分析报告
第二步:判断JVM问题,确定调优目标
根据分析报告,如果GC时间超过1-3秒,或者频繁GC,则进行优化。
第三步:调整参数
- -Xms5g和-Xmx5g的值设置成相等,以避免每次垃圾回收完成后JVM重新分配内存。堆大小默认为-Xms指定的大小,默认空闲堆内存小于40%时,JVM会扩大堆到-Xmx指定的大小;空闲堆内存大于70%时,JVM会减小堆到-Xms指定的大小。
- -Xmn2g:新生代尽量设置大一些,让对象在新生代多存活一段时间,每次Minor GC 都要尽可能多的收集垃圾对象,防止或延迟对象进入老年代的机会,以减少应用程序发生Full GC的频率。持久代一般固定大小为64m。官方推荐配置为整个堆的3/8。
- -XX:SurvivorRatio=6:设置年轻代中Eden区与Survivor区的大小比值。一般一个Survivor区占整个年轻代的1/8。
- -XX:ParallelGCThreads=8:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
- 减少对象创建,内存管理和资源及时释放
第四步:对比调优前后差距
第六步:重复以上步骤,找到最佳JVM参数设置
垃圾回收机制
如何判断对象是否存活?
1.引用计数法
给每一个对象设置一个引用计数器,当有一个地方引用该对象的时候,引用计数器就+1,引用失效时,引用计数器就-1;当引用计数器为0的时候,就说明这个对象没有被引用; 缺点:无法解决循环引用的问题,当A引用B,B也引用A的时候,此时AB对象的引用都不为0,此时也就无法垃圾回收。
2.可达性分析法
从GC Roots对象向下搜索,如果一个对象到GC Roots没有任何引用链相连接时,说明此对象不可用,在java中可以作为GC Roots的对象有以下几种:
- 虚拟机栈中引用的对象
- 方法区类静态属性引用的变量
- 方法区常量池引用的对象
- 本地方法栈JNI引用的对象
垃圾回收算法
1.标记清除法
第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记;第二步:在遍历一遍,将所有标记的对象回收掉;特点:效率不行,标记和清除的效率都不高;标记和清除后会产生大量的不连续的空间分片,可能会导致之后程序运行的时候需分配大对象而找不到连续分片而不得不触发一次GC。
2.复制算法
"复制"算法是为了解决"标记-清理"的效率问题。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一块上面,然后再把已经使用过的内存区域一次清理掉。这样做的好处是每次都是对整个半区进行内存回收,内存分配时也就不需要考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配即可。此算法实现简单,运行高效。复制收集算法在对象存活率较高时会进行比较多的复制操作,效率会变低。
3.标记整理法
标记过程仍与 " 标记 - 清除 " 过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。
4.分代算法(Java 垃圾回收机制原理_java垃圾回收机制原理_野生在江南的博客-优快云博客)
在新生代中,每 次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没 有额外空间对它进行分配担保,就必须采用 " 标记 - 清理 " 或者 " 标记 - 整理 " 算法。