JVM性能调优实战1-OOM

文章目录

  • 1. 场景
  • 2. 使用MAT分析
  • 3. 使用Jprofiler分析
  • 4. 使用VisualVM分析
  • 5. 总结

1. 场景

最近一个线上系统频繁发生OOM,异常信息如下:

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1006)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:881)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:146)
	at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:81)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.OutOfMemoryError: Java heap space

从堆栈信息一般看不出端倪,堆内存分配不够了,实际问题就是:堆空间不足。接下来使用常见性能调优工具逐个分析

2. 使用MAT分析

MAT堆内存分析神奇,能自动帮你分析内存泄露

  • 打开对应的OOM dump快照文件

JVM性能调优实战1-OOM_第1张图片

  • 选择内存泄露疑点报告Leak Suspects Report

JVM性能调优实战1-OOM_第2张图片

  • 查看内存泄露报告

JVM性能调优实战1-OOM_第3张图片

  • 查看详情报告

JVM性能调优实战1-OOM_第4张图片

可以很明显的看到com.socket.WebSocketServer 中的sessionSet字段

  • 通过dominator tree也能看到端倪

JVM性能调优实战1-OOM_第5张图片

  • 查看代码看到如下凶手
public class WebSocketServer {
    
    private static CopyOnWriteArraySet sessionSet = new CopyOnWriteArraySet();
    ...
    ...
}

3. 使用Jprofiler分析

  • 选择一个快照文件

JVM性能调优实战1-OOM_第6张图片

  • 打开快照文件

JVM性能调优实战1-OOM_第7张图片

  • 直接查看大对象

JVM性能调优实战1-OOM_第8张图片

4. 使用VisualVM分析

重要提示: jdk自带的jvisualvm打开很卡,计算保留大小的时候直接卡死,直接去官网下载最新版本visualvm

  • 装入快照文件

JVM性能调优实战1-OOM_第9张图片

  • 选择堆dump快照文件

JVM性能调优实战1-OOM_第10张图片

  • 右下角Dominators by Retained Size中,点击Compute Retained Sizes计算保留大小,才能查看到具体引用的类和实例,保留大小对分析内存分析非常有用,下面我们梳理一下下面三个概念:
浅堆大小(Shallow Heap Size):仅计算对象本身在内存中所占用的空间,不包括引用的其他对象。
保留大小(Retained Size):包括对象自身的大小以及由该对象直接或间接引用的其他对象的大小。
深堆大小(Deep Heap Size):这个术语并非通用标准概念,可能指代不同的含义。一般来说,它可以理解为与"保留大小"类似,包括对象自身的大小以及通过对象引用的其他对象的大小。

JVM性能调优实战1-OOM_第11张图片

  • 然后就能看到问题所在,接着双击查看详情

JVM性能调优实战1-OOM_第12张图片

  • 详情查看,就可以看到类静态变量sessionSet的问题导致

JVM性能调优实战1-OOM_第13张图片

5. 总结

这个线上OOM案例相对比较简单,但的确是真实案例。。。

你可能感兴趣的:(JVM,jvm)