一次线上内存溢出排查

现象:

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

image-20210112083051244

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

image-20210112083225520

image-20210112083235248

发现gc标记时间非常长。

使用sonar扫描发现很多bug,

image-20210112083341654

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

jmap -histo pid

image-20210112083616131

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

image-20210112083715766

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

image-20210112083749637

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

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

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

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

img

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

img

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

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

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

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

image-20210112084410413

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

   /**
     * 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本地压测更改后的代码,观察内存变化和垃圾回收:

image-20210112084623749

image-20210112084633730

image-20210112084644491

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

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

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论