一次线上内存溢出排查

现象:

频繁full gc ,内存回收不掉。

一次线上内存溢出排查

分析:新增需求后发现此现象,使用阿里 的Athas查线上内存情况,

一次线上内存溢出排查

一次线上内存溢出排查

发现gc标记时间非常长。

使用sonar扫描发现很多bug,

一次线上内存溢出排查

但是经过代码分析,原因不在此。

jmap -histo pid

一次线上内存溢出排查

发现linkedHashMap有大量的数据未回收。将异常时转存的dump文件拉下来。

一次线上内存溢出排查

文件太大,只能使用profile来进行分析,漫长等待后:

一次线上内存溢出排查

这里的大对象时来自javax.crypto.JceSecurity。

????如果有使用过加解密的同事会立马发现这个是RSA或者AES中使用的实体类....原先以为的代码问题其实都不是(这个怀疑用时最长)

javax.crypto.JceSecurity导致内存溢出问题分析:

通过分析JceSecurity类源码可知调用verificationResults.put的代码只有JceSecurity.getVerificationResult方法,而调用链上游如下几个地方,都是jdk里面的,可能一下子无从查找:

一次线上内存溢出排查

回到造成内存溢出的直接原因“大量的BouncyCastleProvider实例无法回收”

一次线上内存溢出排查

再开发工具中根据关键字“new BouncyCastleProvider” 搜索相关代码发现有7处代码构造BouncyCastleProvider实例,但继续向上寻找调用链发现最终只有 DesHelper() 是有效的,并且此时的DesHelper已经是我们自定义的代码,DesHelper.encrypt 会调用Cipher.getInstance 方法,与步骤1中的调用链衔接上。

对DesHelper()上游调用链树进行分析

一次线上内存溢出排查又可引出很多业务代码,所以需要尽量保持DesHelper的构造方法签命不变(如果对上游业务代码熟悉,改变方法签命也可以)

所以解决问题的实现方案之一可以如下:

一次线上内存溢出排查

其实这个类已经加了注解说明

   /**
     * Construct a new provider.  This should only be required when
     * using runtime registration of the provider using the
     * <code>Security.addProvider()</code> mechanism.
     */
    public BouncyCastleProvider()
    {
        super(PROVIDER_NAME, 1.59, info);

        AccessController.doPrivileged(new PrivilegedAction()
        {
            public Object run()
            {
                setup();
                return null;
            }
        });
    }

最后的最后:验证正确性;

使用jmeter本地压测更改后的代码,观察内存变化和垃圾回收:

一次线上内存溢出排查

一次线上内存溢出排查

一次线上内存溢出排查

线上程序启动的时候,我们需要增加OOM的时候打印堆栈信息 ,增加

  • -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof即可

小白一个,轻喷;..... 具体细节不在此文章中描述。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
java底层

底层原理

2021-5-12 17:18:58

java

实现api接口签名

2021-5-12 17:24:36

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧