A place to hold mainly reading notes, and some technical stuff occasionally. 这里主要是一些读书笔记、感悟;还有部分技术相关的内容。
目录[-]
线上一台后端服务所在机器CPU
飙到300%多。。这个过程并不是一下子就完成的,而是过几个小时就来一次,奇了怪了。
top -c
jstack -l 32508 > jstack.32508.log
jmap -dump:live,format=b,file=32508-1.bin 32508
MAT:https://www.eclipse.org/mat/downloads.php
以下进行一一说明:
如果条件允许,要保留一个服务进行问题分析。
肯定要看日志的,有没有报错信息等。
top -c
查看对资源消耗严重的进程列表。
jstack -l 32508 > jstack.32508.log
可以看到解密相关线程阻塞。。
jmap -dump:live,format=b,file=32508-1.bin 32508
对线上环境,间隔时间,连续三次dump出堆内存信息。从这几个文件大小也可以看出,随着时间推移,内存占用越来越大,请看下一步。
MAT:https://www.eclipse.org/mat/downloads.php
依次打开上一步导出的三个bin
文件:
可以发现,类javax.crypto.JceSecurity
的实例占据的堆内存持续增加,最终导致OOM
,线程阻塞,CPU
飙升。。
JceSecurity
类中有个Map,verificationResults
是一个静态对象,无法被JVM回收。
问题代码:
Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
Security.addProvider(provider);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
原来是每次用户登录,都有一个`BouncyCastleProvider`对象被放到`IdentityHashMap`中,而这个Map(static)又无法被回收。。
解决方法就是将`BouncyCastleProvider`作为单例,而不是每次解密时都new一个新对象。
private static org.bouncycastle.jce.provider.BouncyCastleProvider bouncyCastleProvider = null;
public static synchronized org.bouncycastle.jce.provider.BouncyCastleProvider getInstance() {
if (bouncyCastleProvider == null) {
bouncyCastleProvider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
}
return bouncyCastleProvider;
}
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", getInstance());
进行本地测试,用到的工具:
JConsole
, JavaVisualVM
(JDK自带)JMeter
(http://jmeter.apache.org/download_jmeter.cgi)测试步骤如下:
1. 本地启动后的服务;
2. `JavaVisualVM` 监听服务的进程;
3. `JMeter`压测解密相关接口;
内存从开始的几百M到多线程压测时的1G+,然后有一部分内存GC
无法回收,最终堆内存占用保持在1G左右。。
将jmap -dump:live,format=b,file=11428.bin 11428
导出的bin
文件,加载至MAT
进行分析。
可以看到与线上环境的问题一样,问题复现完成。问题不重要,这里仅提供解决问题的一个思路。
If you have any questions or any bugs are found, please feel free to contact me.
Your comments and suggestions are welcome!