由多线程内存溢出产生的实战分析
一日凌晨 , 手机疯狂报警 , 短信以摧枯拉朽之势瞬间以百条的速度到达 , 我在睡梦中被惊醒 , 看到短信的部分内容如下:
Caused by: java.lang.OutOfMemoryError: unable to create new native threadat java.lang.Thread.start0(Native Method)at java.lang.Thread.start(Thread.java:597)at java.util.Timer.
看到这个错误 , 我的第一感觉是创建了大量的线程 , 并且资源没有被回收 , 但是报错的却是其中一台应用服务器 , 表象看不太像是程序的问题 , 而此时在凌晨并发量也不应该会有这么大啊?同时我们不能因为报错暂停服务使用 , 而影响商户 , 所以决定要先解决问题 , 于是采用必杀技重启这台服务器 , 观察一小时内存溢出消失 , 问题暂时解决 。
第二天白天这个问题并没有复现 , 我认为这是偶发事件 , 就没有过于在意 , 于是当晚再次出现内存溢出 , 并且还是随机某一台服务器爆出 , 我紧急找到监控部和系统部要求拿到栈信息内容和dump文件 , 然而并没有 。。。。 我们紧急开会做代码走查 , 发现这个应用其实是一个非常简单的应用 , 里面没有使用线程 , 也没有很复杂的逻辑 , 只是简单的增删改操作 , 那会是什么原因呢?
接下来我们的分析之路开始了 。。。。。
现状情况无法找到OOM时的javacore和heapdump文件 。 无法还原问题发生时候系统内存被各个进程使用的占比 , CPU的占比 。 日志没有异常堆栈信息 。
解决思路1、要能够验证Tomcat配置内存溢出时打印堆栈并验证可行性 , 并保证在上线和重启不被擦除 。 现状:当前只配置-XX:+HeapDumpOnOutOfMemoryError” , 没有配置路径 , 不知道是被重启删除还是没有产生 。
【由多线程内存溢出产生的实战分析】2、实现脚本 , 在出现OOM的时候 , 重启前 , 打印jstack栈信息和dump文件 。 现状:目前是人工使用jmap和jstack打印CPU和内存信息给应急人员看 。
3、实现JVM内存监控 , 在JVM内存紧张的时候提前报警 , 人工干预 。 现状:无
4.、监控或者实现脚本收集java进程OOM的时候 , 各个进程对内存的占比等 , 以及监控cache, buffer等 。 现状:根据凌晨OOM的情况 , 错误堆栈表明 , start0是JVM申请系统内存时内存不够 , 并非jvm堆内存不够而导致 , 需要上面信息查看详细的系统 。
5、研究底层 , 寻找java.lang.OutOfMemoryError: unable to create new native thread错误的引发原因有哪些 。
6、针对项目进行压测发现问题点 。
深层次测试研究测试环境
操作系统:centos 7 64bitLinux内核:Linux centos 3.10.0-327.10.1.el7.x86_64配置:1G内存虚拟机工具:virtual box 64bitJDK:
openjdk version “1.8.0_71” OpenJDK Runtime Environment (build 1.8.0_71-b15) OpenJDK 64-Bit Server VM (build 25.71-b15, mixed mode)
代码如下:
/** * PROJECT_NAME: test * CREATE BY:chao.cheng **/public class OOMTest {public static void main(String[] args) throws IOException {if (args.length < 1) {System.out.println("please input thread numbers!");return;}int threadNumber = Integer.parseInt(args[0]);try {for (int i = 0; i < threadNumber; i++) {new Thread() {public void run() {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}.start();System.out.println("Thread " + i);}} catch (Throwable e) {e.printStackTrace();}}}
1、第一次测试过程:输入命令如下:
ulimit -u显示:3584
注: ulimit -u是显示用户最多可开启的程序数目 。
程序JVM参数设置如下:
java OOMTest 4000 -Xmx500m -Xss2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
运行结果如下:
文章插图
查看系统内存如下:
文章插图
结论分析:a、在tmp目录下没有生成dump文件 。 我们需要注意 , 使用-XX:+HeapDumpOnOutOfMemoryError参数的时候 , 并不一定在任何溢出场景下都会产生dump文件 。
b、系统内存还有很多 , 却无法创建线程了 。 感觉是系统中存在的进程/线程已经达到系统配置的极限 。
2、第二次测试过程:
使用ulimit -u 65535命令或者直接修改limits.conf文件 , 将max user process参数修改为65535 。
jvm参数配置如下:
java OOMTest 4000 -Xmx500m -Xss2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
程序运行结果如下:并没有报任何错误
文章插图
修改jvm参数为:
- GB|备货充足要多少有多少,5000mAh+128GB,红米新机首销快速现货
- 占营收|华为值多少钱
- 查询|数据太多容易搞混?掌握这几个Excel小技巧,办公思路更清晰
- 商品|问道自有品牌,山姆多方博弈
- 垫底|5G用户突破2亿:联通垫底,电信月增700万,中国移动有多少?
- 色卡|双人场景/多机位色彩匹配,色卡很重要
- 砍单|iPhone12之后,拼多多又将iPhone12Pro拉下水
- 打响|拼多多打响双12首枪,iPhone12降到“mini价”,苹果11再见
- 不到|苹果赚了多少?iPhone12成本不到2500元,华为和小米的利润呢?
- 机器人|网络里面的假消息忽悠了非常多的小喷子和小机器人