從一個OutOfMemoryError 學會了分析Java內存泄漏問題


以前都是好好的,最近出現了 oom。

問題


開始是: java.lang.OutOfMemoryError: Java heap space
2019-06-14 11:02:41.678 ERROR 13789 --- [nio-8082-exec-3] c.e.p.s.c.c.core.ELDictionaryController  : 系統異常

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:1053) ~[spring-webmvc-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) [spring-webmvc-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) [spring-webmvc-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) [spring-webmvc-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-embed-websocket-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) [spring-web-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.filters.RemoteIpFilter.doFilter(RemoteIpFilter.java:845) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.filters.RemoteIpFilter.doFilter(RemoteIpFilter.java:902) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123) [druid-1.1.5.jar!/:1.1.5]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at com.lkk.platform.system.controller.filter.CORSFilter.doFilter(CORSFilter.java:55) [erdp_system_controller-2.0.0-GA.jar!/:2.0.0-GA]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:679) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_212]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_212]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_212]
Caused by: java.lang.OutOfMemoryError: Java heap space
        at java.util.jar.Manifest.read(Manifest.java:270) ~[na:1.8.0_212]
        at sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:276) ~[na:1.8.0_212]
        at sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:263) ~[na:1.8.0_212]
        at java.util.jar.JarVerifier.processEntry(JarVerifier.java:318) ~[na:1.8.0_212]
        at java.util.jar.JarVerifier.update(JarVerifier.java:230) ~[na:1.8.0_212]
        at java.util.jar.JarInputStream.read(JarInputStream.java:212) ~[na:1.8.0_212]
        at java.util.zip.ZipInputStream.closeEntry(ZipInputStream.java:140) ~[na:1.8.0_212]
        at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:118) ~[na:1.8.0_212]
        at java.util.jar.JarInputStream.getNextEntry(JarInputStream.java:142) ~[na:1.8.0_212]
        at java.util.jar.JarInputStream.getNextJarEntry(JarInputStream.java:179) ~[na:1.8.0_212]
        at org.apache.catalina.webresources.JarWarResourceSet.getArchiveEntries(JarWarResourceSet.java:117) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.webresources.AbstractArchiveResourceSet.getResource(AbstractArchiveResourceSet.java:253) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.webresources.StandardRoot.getResourceInternal(StandardRoot.java:281) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.webresources.Cache.getResource(Cache.java:62) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.webresources.StandardRoot.getResource(StandardRoot.java:216) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.webresources.StandardRoot.getClassLoaderResource(StandardRoot.java:225) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2299) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:863) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.findClassIgnoringNotFound(TomcatEmbeddedWebappClassLoader.java:121) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.doLoadClass(TomcatEmbeddedWebappClassLoader.java:86) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.loadClass(TomcatEmbeddedWebappClassLoader.java:68) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1186) ~[tomcat-embed-core-9.0.16.jar!/:9.0.16]
        at ch.qos.logback.classic.spi.PackagingDataCalculator.loadClass(PackagingDataCalculator.java:204) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.spi.PackagingDataCalculator.bestEffortLoadClass(PackagingDataCalculator.java:228) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.spi.PackagingDataCalculator.computeBySTEP(PackagingDataCalculator.java:135) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.spi.PackagingDataCalculator.populateFrames(PackagingDataCalculator.java:100) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.spi.PackagingDataCalculator.calculate(PackagingDataCalculator.java:58) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.spi.ThrowableProxy.calculatePackagingData(ThrowableProxy.java:142) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:122) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383) ~[logback-classic-1.2.3.jar!/:na]
        at ch.qos.logback.classic.Logger.error(Logger.java:538) ~[logback-classic-1.2.3.jar!/:na]
View Code
512M 不夠嗎? 很有可能啊...
增加內存到1G 后仍然出現問題:Failed to mark a promise as failure because it has failed already: [DefaultChannelPromise@33a99639(failure: io.netty.handler.codec.EncoderException: java.lang.OutOfMemoryError: GC overhead limit exceeded), io.netty.handler.codec.EncoderException: java.lang.OutOfMemoryError: GC overhead limit exceeded
java.lang.OutOfMemoryError: GC overhead limit exceeded

2019-06-17 09:00:42.648  WARN 1993 --- [erverWorker-8-3] o.a.d.r.exchange.codec.ExchangeCodec     :  [DUBBO] Fail to encode response: Response [id=319633, version=2.0.2, status=20, event=false, error=null, result=RpcResult [result=null, exception=org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded]], send bad_response info instead, cause: GC overhead limit exceeded, dubbo version: 2.7.1, current host: 192.168.11.183

java.lang.OutOfMemoryError: GC overhead limit exceeded

2019-06-17 09:00:43.907 ERROR 1993 --- [20884-thread-52] o.a.dubbo.rpc.filter.ExceptionFilter     :  [DUBBO] Got unchecked and undeclared exception which called by 192.168.11.183. service: com.elead.platform.system.domain.service.ELCommonCodeRegulationService, method: GetCode, exception: java.lang.OutOfMemoryError: GC overhead limit exceeded, dubbo version: 2.7.1, current host: 192.168.11.183
View Code

 

這就奇怪了! 注意到 出現次數比較多是 com.lkk.platform.system.domain.service.ELCommonCodeRegulationService, method: GetCode,

    @Transactional(readOnly = false)
    public String GetCode(String name){
        RLock rlock = redissonManager.getRedisson().getLock(name);
        boolean getLock = false;
        try{
            getLock = rlock.tryLock(3, 20, TimeUnit.SECONDS);
            if (getLock){
                ELCodeDef elCodeDef = findCommonCode(name);
                super.updateById(elCodeDef);
                return elCodeDef.getCode();
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            if (getLock) {
                rlock.unlock();
            }
        }
        return "";
    }

而
    @Autowired
    RedissonManager redissonManager;
 

 

分析

由此懷疑這個地方有些問題。 雖然出現了oom, 但是進程沒有死, 似乎依然可以響應某些請求,於是把線程dump 下來, 觀察一番,發現 redisson-netty 竟然有上千個

就是這個

"redisson-netty-25-32" #808 prio=5 os_prio=0 tid=0x00007f7ec0187800 nid=0x3625 runnable [0x00007f7e77d6c000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
    at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
    at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
    - locked <0x00000000e90b27a0> (a io.netty.channel.nio.SelectedSelectionKeySet)
    - locked <0x00000000e90b27f8> (a java.util.Collections$UnmodifiableSet)
    - locked <0x00000000e90b2708> (a sun.nio.ch.EPollSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
    at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
    at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:786)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:434)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)

太不正常了!  但是 這里的redisson-netty- 仍然是 RUNNABLE 狀態, 看起來也不是問題啊!  仔細檢查了下, 也沒發現死鎖啊!!

那就不是線程問題嗎?

 

redisson 的bug 嗎? redisson 的官網 的issue 搜索一番,無果。 郁悶了! 而且我的 redisson 版本是 3.1.1, 已經很新的了吧!!

 

堆棧分析吧!!把java 的heap 拔下來,

 

jps -l,  然后 jmap -dump:format=b,file=dumpFileName pid 

 

看到有些異常:

肯定不是 spring 的classloader 吧。

 

 

 

 

看到 netty 的PoolThreadCache 比較可疑啊, 還有 mybatis。

 

 

 

 

Biggest Top-Level Dominator Packages 跟之前一樣的提示, 一個是netty的 PollThreadCache, 一個是netty 的epoll, 還有是redision, 還有是sun 的EPollArrayWrapper, 還有mybatis,其他 也看不出什么來啊!

 

分析只能到此為止了嗎? io.netty.buffer.PoolThreadCache 是什么東東? 我不熟悉啊!  看過netty 源碼, 已經全忘了!

是內存泄漏嗎?  好像也看不出來。 不太確定。 網上搜索看看吧!!

 

還是從redision 入手吧。  咦, redision 的用法好像不太對哦!!! 改一下吧:

    @Autowired
    RedissonClient redissonClient;
==> @Autowired RedissonManager redissonManager; RLock rlock = redissonClient.getLock(name); ==>  RLock lock = redissonManager.getRedisson().getLock(name);

RedissonManager如下:

import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.ReadMode;
import org.redisson.config.SentinelServersConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Component
public class RedissonManager {

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Value("${spring.redis.password}")
    private String redisPassword;

    @Value("${spring.redis.port}")
    private String redisPort;

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.timeout}")
    private String redisTimeout;

    @Value("${spring.redis.sentinel.node}")
    private String redisSentinelNode;

    @Value("${spring.redis.sentinel.master}")
    private String redisSentinelMaster;

    @Bean
    public RedissonClient getRedisson() {
        Config config = new Config();
        if (StringUtils.isNotEmpty(redisPort)) {
            config.useSingleServer().setAddress("redis://" + redisHost + ":" + redisPort).setPassword(redisPassword);
        } else if (StringUtils.isNotEmpty(redisSentinelNode)) {
            String[] nodes = redisSentinelNode.split(",");
            List<String> newNodes = new ArrayList(nodes.length);
            Arrays.stream(nodes).forEach((index) -> newNodes.add(index.startsWith("redis://") ? index : "redis://" + index));
            SentinelServersConfig serverConfig = config.useSentinelServers()
                    .addSentinelAddress(newNodes.toArray(new String[0]))
                    .setMasterName(redisSentinelMaster)
                    .setReadMode(ReadMode.SLAVE)
                    .setTimeout(Integer.valueOf(redisTimeout));
            if(StringUtils.isNotEmpty(redisPassword)){
                serverConfig.setPassword(redisPassword);
            }
        }
        return Redisson.create(config);
    }
}
View Code

 

改了就好了!!突然自己明白了, 原來就是這個redision 用法錯誤導致的!!

 

不信? 重新拔下來heap dump 分析一下:

最大的 com.mysql.cj.jdbc.AbandonedConnectionCleanupThread 才占用2m, 不是什么問題。 可見已經沒有了什么

PoolThreadCache 已經下滑到了第七位, 總占用7M ,38個對象,看起來正常了許多!! :

 

 

 

總結

花了2天時間終於搞定!!

其實上面的 thread dump 和 heap dump 已經給出了比較明顯的答案了!! 就是 PoolThreadCache 占用了 過多的內存, 其原因就是 PoolThreadCache 錯誤的創建了 太多!————  本來應該是單例的 對象, 被搞成了 prototype, 你說是不是引起了大錯!!! 一個 PoolThreadCache占用內存差不多196,000byte, 921個就 是 180516000 byte 也就是 差不多 下圖的180M, 一類對象就 180M, 總共才1G, 當然會不夠用!!

其實 從錯誤日志也可以 分析出來一些, 在創建需要比較大的內存的對象的時候, 就會出現 oom, 因為內存確實已經不夠了啊!! (這也是為什么 ELCommonCodeRegulationService 的 GetCode 方法調用的時候,出現了很多oom。 但是又不是絕對的。 因為其他 地方也可以創建大內存對象)

其實只要再多問幾個問題就知道了答案:  這個對象為什么出現了這么多次, 占用這么多內存呢??  這個是正常的嗎? 如果能夠很早認識到這些問題,並回答之, 那么問題就不是大問題了,就不會浪費很多時間了!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM