栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

记录一将内存中大量JceSecurity实例导致OOM问题排查

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

记录一将内存中大量JceSecurity实例导致OOM问题排查

问题现象

最近有一个同事负责的项目,运行一断时间就OOM了,让我帮忙排查一下是什么原因,首先我查看项目启动命令配置了dump文件,看到dump文件居然有3.4G,肯定是代码有问题导致内存没有回收。

-XX:+HeapDumpBeforeFullGC -XX:+HeapDumpAfterFullGC -XX:HeapDumpPath=dump

排查过程

选择Biggest Objects,看到javax.crypto.JceSecurity这个对象居然有1.9G

选择javax.crypto.JceSecurity 然后 选择Use Selected Objects,References 里选择Outgoing references

然后可以看到verificationResults 这个HasMap里居然有14349个BouncyCastleProvider对象,看来就是这个对象没有释放导致内存泄露了。
e
在项目里搜索了一下BouncyCastleProvider,这个类是在RSAUtill被实例化了,看来是在做RSA加解密时出现的问题


RSA解密代码如下,应该就是 Cipher.getInstance(“RSA”,new org.bouncycastle.jce.provider.BouncyCastleProvider()) 这行代码导致的

	public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {   
	    try {   
	    	//这里是RSA解密,每解密一次创建了一个对象
	        Cipher cipher = Cipher.getInstance("RSA",   
	                new org.bouncycastle.jce.provider.BouncyCastleProvider());   
	        cipher.init(cipher.DECRYPT_MODE, pk);   
	        int blockSize = cipher.getBlockSize();   
	        ByteArrayOutputStream bout = new ByteArrayOutputStream(64);   
	        int j = 0;
	        while (raw.length - j * blockSize > 0) {   
	            bout.write(cipher.doFinal(raw, j * blockSize, blockSize));   
	            j++;   
	        }   
	        return bout.toByteArray();   
	    } catch (Exception e) {   
	        throw new Exception(e.getMessage());   
	    }   
	}   

进入 Cipher.getInstance()方法中,关键有一行代码 Exception ve = JceSecurity.getVerificationResult(provider);

    public static final Cipher getInstance(String transformation,
                                           Provider provider)
            throws NoSuchAlgorithmException, NoSuchPaddingException
    {
        if (provider == null) {
            throw new IllegalArgumentException("Missing provider");
        }
        Exception failure = null;
        List transforms = getTransforms(transformation);
        boolean providerChecked = false;
        String paddingError = null;
        for (Transform tr : transforms) {
            Service s = provider.getService("Cipher", tr.transform);
            if (s == null) {
                continue;
            }
            if (providerChecked == false) {
            	//此处获取了provider
                Exception ve = JceSecurity.getVerificationResult(provider);
                if (ve != null) {
                    String msg = "JCE cannot authenticate the provider "
                        + provider.getName();
                    throw new SecurityException(msg, ve);
                }
                providerChecked = true;
            }
            if (tr.supportsMode(s) == S_NO) {
                continue;
            }
            if (tr.supportsPadding(s) == S_NO) {
                paddingError = tr.pad;
                continue;
            }
            try {
                CipherSpi spi = (CipherSpi)s.newInstance(null);
                tr.setModePadding(spi);
                Cipher cipher = new Cipher(spi, transformation);
                cipher.provider = s.getProvider();
                cipher.initCryptoPermission();
                return cipher;
            } catch (Exception e) {
                failure = e;
            }
        }

        // throw NoSuchPaddingException if the problem is with padding
        if (failure instanceof NoSuchPaddingException) {
            throw (NoSuchPaddingException)failure;
        }
        if (paddingError != null) {
            throw new NoSuchPaddingException
                ("Padding not supported: " + paddingError);
        }
        throw new NoSuchAlgorithmException
                ("No such algorithm: " + transformation, failure);
    }

在JceSecurity.getVerificationResult(provider) 找到了 verificationResults.put(p, PROVIDER_VERIFIED),每解密一次,就会像verificationResults对象中put一次provider对象,怪不得verificationResults 这个HasMap里居然有14349个BouncyCastleProvider对象。原来都找到了。

解决方法

将 Cipher 改写成单例就好了

    private static Cipher cipher;
    static {
        try {
            cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
提示

javax.crypto.Cipher 自带的JDK没有原码 ,经查阅资料发现,jdk加密相关的类javax.crypto.Cipher不提供源码文档,原因是受美国政府强加密算法出口管制限制而不提供源代码。美国政府对涉及到加密算法和强加密的软件技术产品进行出口管制和限制,基于这个法律的要求,JDK 不能提供有关强加密的源代码。不过可以从 https://jdk.java.net/java-se-ri/8-MR3 下载

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/721328.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号