由多线程内存溢出产生的实战分析( 二 )
java OOMTest 40000 -Xmx500m -Xss2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
配置只是修改了创建线程数为40000 , 比上一次4000多了10倍 。
程序运行结果如下:
文章插图
查看内存如下:
文章插图
结论分析:此时已经把系统的内存全部耗尽 , 无法使用free、top命令 , 此时已经无法执行任何命令
3、综合分析a、当max user processers 设置的较小的时候 , 影响系统线程数目的是max user processers的设置b、当max user processers设置为65535的时候 , 影响系统线程数目的是系统的内存 。 c、对外的异常信息均为:OOM:unable to create native thread 。
多线程内存溢出的理论支撑通过上面的分析 , 我们看到其实多线程内存溢出有很大原因是因为系统设置和内存大小造成的 , 那么我们如何来分析当前系统配置能够支持多少线程呢?
对于java中的线程 , 我之前的理解一直是在java中new新线程的时候是直接使用jvm的内存 , 可实际情况却不是这样的 。 在java中每个线程需要分配线程内存 , 用来存储自身的线程变量 , 在jdk1.4中每个线程是256K的内存 , 在jdk1.5中每个线程是1M的内存 , jdk1.6以上版本不太清楚 。 在java中每new一个线程 , jvm都是向操作系统请求new一个本地线程 , 此时操作系统会使用剩余的内存空间来为线程分配内存 , 而不是使用jvm的内存 。 这样 , 当操作系统的可用内存越少 , 则jvm可用创建的新线程也就越少 , 我们举一个例子如下:
总内存 -Xmx -Xms -Xss 剩余内存 线程数1024M 256M 256M 1M 768M 768根据上面的知识 , 于是衍生出了下面这样的通用公式:(MaxProcessMemory – JVMMemory – ReservedOsMemory) / (ThreadStackSize) = Number of threads注:MaxProcessMemory:进程最大寻址空间 。 JVMMMEMORY:jvm的内存空间(堆+永久区)-Xmx大小 (应该是实际分配大小)ReservedOsMemory:操作系统预留内存ThreadStackSize:-Xss大小
信息文件的导出文章开始的时候说过 , 在内存溢出的时候 , 因为服务器重启导致jstack内容消失了 , 虽然配置了jvm参数HeapDumpOnOutOfMemoryError , 但并没有产生相应的dump文件 , 于是我们采用脚本导出的方式 , 内容如下:
#!/bin/bashps -Leo pid,lwp,user,pcpu,pmem,cmd > /tmp/ps.logecho 1pid=`ps aux|grep tomcat|grep xxx|awk -F ' ' '{print $2}'`echo 2pstack $pid >/tmp/pstack.logecho 3lsof > /tmp/sys-o-files.logecho 4lsof -p $pid > /tmp/service-o-files.logecho 5jstack -l $pid> /tmp/js.logecho 6 free -m >/tmp/free.logecho endvmstat 2 1
在系统异常的时候 , 监控系统能够自动调用脚本产生信息文件 , 有了这些文件分析问题才能够得心应手 , 不然出了问题根本无从查起 , 只能是没头苍蝇乱撞 。
压测分析在压测环境中配置与生产环境一样的硬件环境和配置环境进行压测 , 可以看到如下的测试图:
文章插图
通过压测分析 , 在程序并发线程达到1010个的时候 , 就报出unable to create new native thread异常 , 查看上面这张图其实不难看出 , 应用程序中并没有使用线程 , 但是在Log4j中却大量的使用了synchronized这个关键字 , 在并发非常高的时候会产生非常多的阻塞 , 最终内存资源耗尽报出内存溢出错误 。
问题解决方案问题解决方案1、优化程序 , 减少没用的log4j日志输出 , 将log4j日志改为异步+buffer的模式 。 2、单台服务器本身性能有限 , 通过增加服务器的方式提高扩展性 。 3、将系统的一些限制属性增大 , 如:ulimit -a 。
通用解决方案上面的四点是针对我们的解决方案 , 但通过以上的这些分析 , 我们不难发现所有的unable to create new native thread的错误异常都有其共性的地方 , 那接下来我再总结一些相对通用一点的方法帮助大家在以后遇到类似的问题的时候 , 能够有据可查知道如何进行逐步的排查 。
1、当发现这个错误的时候 , 第一时间要排查程序是否有bug , 是否大量的创建了线程 , 或者没有正确使用线程池 , 比如:是否使用了Executors.newCachedThreadPool()方法 , 该方法能创建Integer最大值个线程 , 创建到一定程度的时候系统资源耗尽就会报错 。
2、如果发现程序中并没有使用线程却依然报这个错 , 那么观察一下这个时刻的并发情况如何 , 要是溢出的这一时刻比其他时候并发量都要大 , 这时先查看一下系统资源的情况 , 使用ulimit –a查看max user processes和open files这二个属性的值越大 , 能创建的线程数也就越大 。 3、如果以上二个属性调大依然报错的话 , 说明此时受限于系统内存资源了 , 要是服务器本身内存就比较小的话 , 建议增加内存 。 要是服务器内存比较大 , 就需要通过调整jvm参数来增加线程使用的内存 , 比如减小-Xss值 , 这个值越小能创建的线程数也就越多 , 也可以适当减少-Xmx和-Xms的值 , 增加堆外内存的容量 。
- GB|备货充足要多少有多少,5000mAh+128GB,红米新机首销快速现货
- 占营收|华为值多少钱
- 查询|数据太多容易搞混?掌握这几个Excel小技巧,办公思路更清晰
- 商品|问道自有品牌,山姆多方博弈
- 垫底|5G用户突破2亿:联通垫底,电信月增700万,中国移动有多少?
- 色卡|双人场景/多机位色彩匹配,色卡很重要
- 砍单|iPhone12之后,拼多多又将iPhone12Pro拉下水
- 打响|拼多多打响双12首枪,iPhone12降到“mini价”,苹果11再见
- 不到|苹果赚了多少?iPhone12成本不到2500元,华为和小米的利润呢?
- 机器人|网络里面的假消息忽悠了非常多的小喷子和小机器人