Java-第十四部分-JVM-OOM案例和性能测试案例

JVM全文

OOM案例

堆溢出

  • visualVM分析dump文件
  • 找到导致报错的位置

image.png

  • 找到大量生成的对象

image.png

  • mat分析
  • 找到泄漏疑点

image.png image.png

  • 线程信息

image.png

  • 存放大量对象所导致

image.png

  • Ergonomics 自适应调节所导致

image.png

  • GCEasy查看GC效果

image.png

原因

  • 可能存在大对象分配
  • 可能存在内存泄漏,导致多次GC之后,无法找到一块大的内存容纳当前对象

解决方案

  • 检查大对象的分配
  • 分析dump文件
  • 使用-Xmx加大对内存
  • 是否有大量自定义的Finalizable对象,由框架内部提供

元空间移除

  • 参数
-XX:+PrintGCDetails
-XX:MetaspaceSize=60m
-XX:MaxMetaspaceSize=60m
-Xss512K
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdump4.hprof
-XX:SurvivorRatio=8
-XX:+PrintGCDateStamps
-XX:+TraceClassLoading
-XX:+TraceClassUnloading
-Xms50M
-Xmx50M
-Xloggc:log/gc-oom4.log
复制代码
  • visualVM

image.png

  • jstat -gc id 查看gc情况

YGC 次数 YGCT 时间 GCT 总时间

image.png

  • 错误代码

image.png

  • mat 直方图以包的形式查看产生的大量对象

image.png

原因

  • 运行期间生成大量的代理类,无法卸载
  • 应用长时间运行,没有重启
  • 元空间设置较小

解决方式

  • 检查元空间大小
  • 检查代码中是否有大量发射操作
  • dump文件检查是否存在大量由反射生成的代理类

GC overhead limit exceeded

  • jdk6之后,GC过于频繁,98%的时间在GC,但是回收不到2%的堆内存,回收效果差
  • 设置参数-XX:-UseGCOverheadLimit 禁用这个检查,报堆溢出
  • 参数
-XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/heapdumpExceeded.hprof
-XX:SurvivorRatio=8
-Xms10M
-Xmx10M
-Xloggc:log/gc-comExceeded.log
复制代码

解决方法

  • 是否有大量的死循环,或者大内存的代码
  • dump检查是否存在内存泄漏,加大内存

线程溢出

  • unable to create new native Thread
  • 创建大量线程
  • 创建线程数量的计算公式
  1. (MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
  2. MaxProcessMemory 进程可寻址的最大空间 64位操作系统 2^64
  3. JVMMemory JVM内存
  4. ReservedOsMemory 保留的操作系统内存
  5. ThreadStackSize 线程栈大小

性能调优

Jmemter

export JMETER_HOME=/Users/mzx/Desktop/java/jvm/apache-jmeter-5.4.3
export PATH=$JAVA_HOME/bin:$PATH:.:$JMETER_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JMETER_HOME/lib/ext/ApacheJMeter_core.jar:$JMETER_HOME/lib/jorphan.jar:$JMETER_HOME/lib/logkit-2.0.jar
复制代码
  • 添加线程组,模拟访问

image.png

  • 设置线程组参数

image.png

  • 添加线程请求

image.png

  • 添加监听器

image.png

吞吐量

image.png

  • 提高堆内存,减少fullgc的频率,提高吞吐量

JIT优化

  • 对于只执行一次的代码,用解释器效率更好
  1. 只被调用一次,类的构造器
  2. 没有循环的代码
  3. 只执行少量次数的代码
  • 没有进行逃逸分析 -XX:-DoEscapeAnalysis,产生大量对象

image.png

  • 打开逃逸分析,实际上没有那么多对象

image.png

  • 同步省略,字节码并不会直接省略,还是在编译期间进行优化

image.png image.png

  • 打开标量替换-XX:+EliminateAllocations,逃逸分析才有意义,jvm的栈上分配,实际上就是标量替换

开启逃逸分析,但是没有开启标量替换

image.png

开启逃逸分析,开启标量替换

image.png

没有开启逃逸分析

image.png

  • 小结
  1. 如果经过逃逸分析后,没有一个对象是不逃逸的,那么逃逸分析的过程就是浪费的
  2. 无法保证逃逸分析之后,性能一定会提升

合理配置堆内存

  • Xmx和Xms设置为老年代存活对象的3-4倍,即FullGC之后老年代内存占用的3-4倍
  • 方法区大小设置为老年代存活对象的1.2-1.5倍
  • 年轻代 Xmn设置为老年代存活对象的1-1.5倍
  • 强制触发FullGC
  1. jmap -dump:live,forma=b,file=heap.bin <pid> 生成当前存活对象的dump文件
  2. jmap -histo:live <pid> 打印class的实例数目,内存占用,类全名等信息,加上live后,只会统计存活对象的数量
  3. 测试工具
  • 估算YGC频率

image.png

  • 面对大流量,低延迟的系统,不建议启用UseAdaptiveSizePolicy

CPU占用

  • 服务器上进行
  • top -Hp 16195 进程下进行的线程占比情况

image.png

  • jstack 16195 > jstack.log 保存进程的线程信息
  • 需要根据pid的十六进制找对应的进程

image.png

  • jstack 16195 | grep -A20 3f44 直接找出线程后20行的内容

image.png

  • 死锁解决
  1. 调整锁的顺序,保持一致
  2. 采用定时锁,一段时间后,还不能获取锁,就释放本身持有的锁

G1并发执行的线程数对性能的影响

  • ConcGCThreads并发标记的线程数,最多也就是用户线程的1/4
  • 可以提高吞吐量

调整垃圾回收器

  • G1提高吞吐量

日均百万订单系统

  • 吞吐量

尚硅谷_宋红康_案例7:日均百万订单系统JVM参数设置.png

  • 响应时间控制在100ms
  1. G1设置最大stw时间,设置Java堆占用率阈值

其他问题

  • 12306减库存和订单,同时异步进行

分布式本地库存+单独服务器做库存均衡

  • 50万PV资料类网站,服务器和性能增加,效率更低
  1. 原网站内存有限,频繁进行GC,STW比较长,响应时间比较慢
  2. 之所以更卡顿,内存空间越大,花费FGC时间更长,延迟时间更长
  3. 垃圾回收器,优先考虑G1,在延迟可控的情况下尽可能提高吞吐量;调整配置参数,MaxGCPauseMillisConcGCThreads和堆空间分配;dump文件分析,优化内存空间比例
  • CPU经常100%,调优过程
  1. top -Hp 16195 进程下进行的线程占比情况
  2. jstack 16195 > jstack.log 保存进程的线程信息
  3. jstack 16195 | grep -A20 3f44 直接找出线程后20行的内容
  • 系统内存飙高
  1. 具体工具,内存占比,日志情况
  2. dump文件
  • 监控jvm
  1. 命令行工具
  2. 图形化界面

猜你喜欢

转载自juejin.im/post/7049667285497167903