• 内存快照排查OOM,加密时错误方法指定provider方式错误引起的OOM


    OOM问题一般都是人工代码失误, 多数其实在review阶段应该可以排除,本文主要是想记录下内存快照排查OOM的一个过程 

    场景: 系统的交互安全完全依赖各种加密做(做到了无session,完全WEB无状态,这个设计以后可讲下),故加密变得很重要,但因为有新的加密引入了BouncyCastleProvider。故有修改,测试机一台机器上线一段时间后,运行一段时间后,系统变得非常缓慢,并到最后出现了OOM,最终产生内存快照文件。

    分析:

    JVM内存分析:观察JVM内存,发现大量OU被使用无法释放引起频发GC,导致系统缓慢(基本不可用)

    内存快照分析OOM的原因, 对于这种内存分析最好的方法是dump两份,一份正常的,一份是发生OOM时候的进行对比

    故障重现:

    首先给1G内存给Tomcat,  当OOM时输出日志, JVM参数相关配置 如下:

     -Xms1024m -Xmx1024m -Xmn512m -XX:PermSize=128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/log/dump/ 

    正常启动后,先dump份正常的用于后面对比分析(拿OOM后产生的快照和正常的快照进行对比可以很方便找出泄漏的对象)

    文件导入Eclipse Memory Analyzer

    发现基本是Finalizer占用了内存

    说明:JAVA创建对象时,会新建一个额外的Finalizer 对象指向新创建的对象。 而回收时,至少需要经过两次GC,所以这个是正常的一个内存

    然后开始压测,2000次请求后,系统后面发现越来越慢,基本不可用了,查看GC,发现基本所有内存都满了,一直在GC

    继续说快照, 此时再把内存dump下来导入,对比

    打开快照,从全局图查看已经很明显能找到问题,内存都被JceSecurity占用了861M,可以确定是JceSecurity的问题。

    进一步分析,一般用到下面3个报表

    1, 查看全局内存

    2, 内存泄漏分析

    3, 大对象排序

    这里直接用第2个,查看泄漏分析报告

    96%的内存被JceSecurity这个类占用了

    发现问题对象后,查看Details

     显示这个类的基本情况,

    是通过GC是ROOT追溯的最短路径,这个可以追溯到问题代码的类树的结构,并找到最终引用的代码中

    可以最终找到直接引用类, 这里最终存在类 javax.crypto.jceSecurity的变量, verificationResults里面

    2, 查看verificationResults里的内容

    说明:

    with outgoning references  查看它引用了哪些内容,可以看成是 集合包含了哪些内容

    with incomming references 查看引用它的类

    说明:

    Shallow heap   是对象的自身占用大小

    Retained Heap 是对象包含内容的的总大小

    发现内存基本被它里面的一个BouncyCastleProvider占用。

    最后根据快照能得出 : 

    导致问题的地方在,类javax.crypto.jceSecurity的变量verificationResults里存的org.bouncycastle.jce.provider.BouncyCastleProvider

    代码寻找:

    根据上面找到代码中有用到org.bouncycastle.jce.provider.BouncyCastleProvider的地方,发现项目中的类CryptoUtil 

    工具类提炼出来;

     

    关键是这句,每次都new了个BouncyCastleProvider

    Cipher cipher = Cipher.getInstance(Constants.CRYPOTO_NAME, new BouncyCastleProvider());

    这个类,代码来自7u40-b43 

    从 cipher.init进去

    这段用于验证提供安全的provider对象是否是JCE可以信任的JAR,并把缓存结果存起来,将把该种provider当key存入一个静态的MAP中缓存起来, 故可以理解一到OLD区也未回收.

    verificationResults的验证

     

    值得说明的是,如果这里是HashMap而不是IdentivHashMap的话也是不会内存泄漏的,为什么这样说呢?

    如果它这用的是HashMap, 不如new多少个BouncyCastleProvider, 在HashMap中也是一个,代码证实下:

    复制代码
    /**
     * 小测试,用于说明下Provider与IdentityHashMap的分别特殊处理
     * @author 包子(何锦彬) 2017.01.20
     *
     */
    public class Test {
        public static void main(String[] args) {
            Map hashMap=new HashMap();
            hashMap.put( new BouncyCastleProvider(), "provoder1");
            hashMap.put( new BouncyCastleProvider(), "provoder2");
            System.out.println(hashMap.size()); //输出是1
            
            Map identityMap=new IdentityHashMap();
            identityMap.put( new BouncyCastleProvider(), "provoder1");
            identityMap.put( new BouncyCastleProvider(), "provoder2");
            System.out.println(identityMap.size()); //输出是2
            
        }
    }
    复制代码

     原因如下:

    1, java.security.Provider的hashCode和equals方法是有做特殊处理的,在provider中,只要密钥相等,两个Provider的比较是相等的

    2,IdentivHashMap这个类和HashMap的主要区别是,PUT时判断两个KEY是否相等用的是==

    IdentivHashMap代码如下:



    而HashMap的这里是:

    HashMap 里用的是 if (e.hash == hash && ((k = e.key) == key || key.equals(k))),比较的是hashCode与equals

    解决

    1.其实只要把指定方式由

    Cipher cipher = Cipher.getInstance(Constants.CRYPOTO_NAME, new BouncyCastleProvider());

    改成

    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

    2, 把BouncyCastleProvider改成单例模式

    问1:

    至于为什么要要指定用BouncyCastleProvider 

    http://blog.csdn.net/defonds/article/details/42775183 

    就OK,或者直接删除不指定

    问2:

     其实还可以打印下存活对象, jmap -histo 7276 > dump.txt, 已经能看出些端倪

     

    持续更新留言问题,解答疑问

    最近开始总结内存方面的东西,已经总结以前遇到的一些内存案例分享下,接下来还有几篇,然后是进程/线程相关的,逐渐形成我的知识体系树

    如果你有兴趣,可以文章末尾的公众号二维码一起梳理这些信息。

    欢迎关注我的公众号,重现线上各种BUG, 一起来构建我们的知识体系

     或搜 “包子的实验室”

  • 相关阅读:
    echarts事件触发多次解决办法
    nginx代理出现的问题
    iframe页面之间通信
    获取当前日期的前XX天或者后XX天
    获取本月往后一年的日期月份信息
    Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)D. Take a Guess
    Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)C. Compressed Bracket Sequence
    Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)B. Take Your Places!
    Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2) A. A Variety of Operations
    centos 查看mysql数据库命令
  • 原文地址:https://www.cnblogs.com/springsource/p/6329465.html
Copyright © 2020-2023  润新知