垃圾回收调优
查看虚拟机运行参数
"jdk1.9\bin\java" -XX:+PrintFlagsFinal -version | findstr "GC"
调优领域 内存,锁竞争,cpu占用,ParallelGC,
确定目标: 低延迟还是高吞吐量,选择合适的回收器,
低延迟 CMS,G1(jdk9默认),ZGC(jdk12体验版)
高吞吐 ParallelGC
最快的GC是不发生GC
考虑: 数据是不是太多?
如果mysql查询方式不对,直接遍历大表,会将数据都读到java内存
要分页查询
数据表示是否太臃肿
加载查询了一些不必要的数据,浪费了堆内存
对象的大小,如Integer要占用24个字节,而int只要4个.积少成多嘛
是否有内存泄漏
如静态集合不停添加对象,
用软引用或弱引用,还可以
用redis等第三方,减轻堆压力
新生代调优: 新生代的优化空间更大
新生代特点:
所有的java操作的内存分配非常廉价
TLAB thread-local allocation buffer:线程局部分配缓冲区
作用: 让每个线程用私有伊甸园分配对象内存,避免同时创建对象产生冲突
死亡对象的回收代价是0: 复制算法只复制幸存对象,其他的都是释放
大部分对象只用一次:
MinorGC的时间远远低于FullGC
新生代空间是不是越大越好? -Xmn
新生代小了,就会导致minorGC太频繁
新生代太大,老年代就会相应的减小,最终会导致老年代清理的FullGC变频繁,回收时间更长
Oracle建议: 新生代内存要在堆内存 25%~50% 之间
理想情况: 新生代能容纳所有(并发量*(请求-响应))的数据
新生代中幸存区要大到能保留(当前活跃对象+需要晋升对象)
如果幸存区太小,内容不够时,对象会被提前晋升老年代
晋升阈值配置得当,让长时间存活的对象尽快晋升
-XX:MaxTenuringThreshold=threshold 设置晋升阈值
-XX:+PrintTenuringDIstribution 打印晋升详细信息
CMS老年代调优:
CMS的老年代内存越大越好
最好先不要调优,如果程序正常运行一段时间没有FullGC或者不是因为老年代空间导致FullGC
当其他方式都用了还不行再试试调整老年代空间大小,将老年代空间调大1/4~1/3
-XX:CMSInitiatingOccupanyFraction=percent 老年代垃圾回收阈值(占老年代空间比例)
一般来说设置为 0.75~0.8
案例: FullGC和MinorGC频繁
GC频繁说明空间紧张,如果新生代内存紧张,就会导致新生代晋升阈值降低,导致情况恶化
先试着增大新生代空间,让minorGC那么频繁,晋升阈值同时也会上升,老年代也会好起来
案例: 请求高峰期发生FUllGC,单次暂停时间很长(CMS)
CMS阶段: 初始标记,并发标记,重新标记,并发清理
其中: 初始标记和并发标记都是比较快的
重新标记一般比较慢
重新标记的时候,不仅要扫描新生代,还有扫描老年代,如果新生代对象比较多,数据会比较长
可以在重新标记前,先清理新生代,就可以减少重新标记的负担
-XX:+CMSScavengeBeforeRemark 重新标记前,先进行一次MinorGC
案例: 老年代充裕情况下,发生FullGC(CMS)(jdk1.7)
1.7用的是永久代,1.8用的是元空间
永久代的内存不足,也会导致FUllGC