由多线程内存溢出产生的实战分析

一日凌晨 , 手机疯狂报警 , 短信以摧枯拉朽之势瞬间以百条的速度到达 , 我在睡梦中被惊醒 , 看到短信的部分内容如下:
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.(Timer.java:154)看到这个错误 , 我的第一感觉是创建了大量的线程 , 并且资源没有被回收 , 但是报错的却是其中一台应用服务器 , 表象看不太像是程序的问题 , 而此时在凌晨并发量也不应该会有这么大啊?同时我们不能因为报错暂停服务使用 , 而影响商户 , 所以决定要先解决问题 , 于是采用必杀技重启这台服务器 , 观察一小时内存溢出消失 , 问题暂时解决 。
第二天白天这个问题并没有复现 , 我认为这是偶发事件 , 就没有过于在意 , 于是当晚再次出现内存溢出 , 并且还是随机某一台服务器爆出 , 我紧急找到监控部和系统部要求拿到栈信息内容和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参数设置如下: