AES加密解密内存溢出:OutOfMemoryError: Java heap space 解决
1.背景
之前为某同事写了一个很小的工具,用于:批量将指定目录下的文件进行AES加密和解密。
同事用了一段时间。
今天同事说:报错了。
2.报错信息
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:956)at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:845)at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)at javax.crypto.Cipher.doFinal(Cipher.java:2165)...
3.错误分析
4.1.日志分析
从日志很容易分析出报错原因:内存溢出。
找到错误代码之处:
/*** 对byte[]进行AES加密解密* @param strKey 密钥* @param encryptType 加密还是解密:Cipher.ENCRYPT_MODE Cipher.DECRYPT_MODE* @param oriBytes 原始文件的byte[]数组* @return 加密或解密后的文件byte[]数组*/
private static byte[] AESEncodeAndDecode(String strKey,int encryptType, byte[] oriBytes){byte[] result = null;try {//指定为AES加密,不区分大小写KeyGenerator keygen = KeyGenerator.getInstance(AES);//根据传入的key,生成随机流keygen.init(128,new SecureRandom(strKey.getBytes()));//产生原始对称keySecretKey originKey = keygen.generateKey();//获取原始对称key的字节数组byte[] keyBytes = originKey.getEncoded();//生成AES密钥SecretKey key = new SecretKeySpec(keyBytes,AES);//生成密码器Cipher cipher = Cipher.getInstance(AES);//初始化为加密器cipher.init(encryptType,key);//加密result = cipher.doFinal(oriBytes);} catch (Exception e) {//...} finally{//...}return result;
}
具体报错代码:
//加密
result = cipher.doFinal(oriBytes);
3.2.当前加密策略分析
当前的加密策略:
- 将文件转换成byte[]。
- 对byte[]数组进行AES加密或解密。
- 将加密或解密完成的byte[]转换成文件。
3.3.分析结论
结合报错信息与加密策略,分析得出:
- 报错原因:内存溢出
- 具体原因:在使用Cipher进行加密解密时,处理的byte[]数组超出了内存限制。
验证:
让同事使用较小的文件进行加解密,并未报错。
4.错误解决
由上面的结论可知,原来的加密策略并不可用。因为,只要文件超过一定大小,都会内存溢出。
因此,尝试采用流的方式进行加密,即:CipherOutputStream与CipherInputStream。
对加密进行测试,代码如下:
/*** 通过AES 以流的形式 对文件进行加密* @param strKey* @param oFileName 源文件路径* @param rFileName 目标文件路径* @return*/
public static boolean AESEncodeFileByStream(String strKey, String oFileName, String rFileName) throws Exception {//指定为AES加密,不区分大小写KeyGenerator keygen = KeyGenerator.getInstance(AES);//根据传入的key,生成随机流keygen.init(128,new SecureRandom(strKey.getBytes()));//产生原始对称keySecretKey originKey = keygen.generateKey();//获取原始对称key的字节数组byte[] keyBytes = originKey.getEncoded();//生成AES密钥SecretKey key = new SecretKeySpec(keyBytes,AES);//生成密码器Cipher cipher = Cipher.getInstance(AES);//初始化为加密器cipher.init(Cipher.ENCRYPT_MODE,key);//定义缓存区,用于存储字节流byte[] buffer = new byte[1024];//定义输入流InputStream in = new FileInputStream(oFileName);//定义输出流OutputStream out = new FileOutputStream(rFileName);//定义加密输出流CipherOutputStream cipherOut = new CipherOutputStream(out,cipher);//定义游标int index;//读写while ((index = in.read(buffer)) != -1){cipherOut.write(buffer,0,index);}//关闭IO流cipherOut.close();in.close();return true;
}
经测试,分别对大小为900MB和2.38GB的文件进行加密,均成功。
5.总结
从这个问题,看文件和文件流:
- 直接处理文件:需要把整个文件加载到内存中,当文件很大时,这种方式十分不可行。
- 用文件流处理文件:能够分阶段将相应的数据写入缓冲区,不仅能够解决内存溢出的问题,而且极大地提高了相应的操作效率。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
