java.lang.OutOfMemoryError: unable to create new native thread问题排查以及当前系统最大进程数量
1. 问题描述
线上某应用出问题,查看日志

这一组服务器是2台,每台都有。配置为64G,使用7G,空余内存非常多
2. 问题排查
环境变化:程序迁移到新机器,新机器是CentOS 7,程序运行账号由原来的root改为work。硬件配置由32G升级为64G。
首先切换到work账号,然后运行一个测试程序就是建立线程,发现只能跑2900多个,我的笔记本还能跑2000多个呢,这显然不对。然后在java –Xms2g 来运行,结果一样,这就说明不是JVM内存不够问题。肯定是哪里有系统限制。
测试程序代码如下:

编译代码,它会在当前目录生成一个类文件,名称就是你定义的第一个类

java 类名 即可
java –Xms2g 类名 指定内存大小
无论你是否设置2g内存,运行线程数量都上不去,这就说明不是JVM内存不过,因为线程消耗的是系统内存,那么系统内存充裕但是依然创建失败,所以一定是其他方面有问题。因为任何系统可以运行的线程数量都有有限的。在JAVA语言里,你创建一个线程,会在JVM内存创建一个内存对象的同时创建一个操作系统线程,而这个系统线程的内存不是使用JVM内存的,而是使用系统中剩下的内存建立的。也就是你给JVM内存越多,那么你能创建的线程数就越少,也就是越容易发生这个异常。
(MaxProcessMemory – JVMMemory – ResverdOsMemory)/ ThreadStackSizk = 线程数量
MaxProcessMemory:一个进程最多使用的内存大小
JVMMemory:JVM的Heap大小,也就是堆,因为你也只能设置堆的大小,这个的值是堆的最小值+PermGen的大小
ResverdOsMemory:操作系统保留的内存,也就是内核使用的,一般为120M。
ThreadStackSizk:线程栈的大小,单位为字节byte 所以上面的公式要统一换成字节来计算。系统有64G内存,就算不适用公式计算也应该知道内存是绝对够用的。那到底是什么问题?
怀疑到了系统限制,通过使用ulimit –u来查看最大进程数量是16384,这显然也很大,因为通过pstree –p | wc –l 进行统计,当前系统才几千个线程,离16384还很远。但是我是在root账号下运行的ulimt –u,突然想到不同账号会不会有不同限制,果断切换到work账号,运行ulimit –u发现结果是4096,而这时再运行pstree –p | wc –l 统计结果为3800多个。也就是说work账号可用线程数量不太多了。这也就是为什么在work账号下运行测试程序即使你更改了JVM参数结果还是一样的。这时候就基本定位到问题,然后修改系统参数

然后再次切换到work账号运行程序,发现可以跑到1.5万,线程明显上升。这时候再运行ulimt –u发现值和root账号的结果是一样的。
需要注意在/etc/secriyt/limits.cof中也可以设置这个,但是如果你在limits.d下面的配置文件也设置了同样的名称只是值不同,那么它会取2个值中最小的。
重点:虽然对work和root设置了unlimited,但是也不意味着可以无限增长,因为这个最大可用量是系统决定的,它的具体值受到物理内存页个数等限制。
解决上面问题的通常办法是:
- 使用64位系统,这样意味着可以使用更多物理内存
- 减少给JVM分配的内存,这样就有更多可用物理内存给线程,不过现在服务器级别都是64G内存起,这个JVM内存也可以不设置或者给它最多2G大小,这个取决于具体你物理机跑多少个JVM实例所决定。
- 减少单个线程的栈大小(-Xss 参数设置)
如果上面的办法不行,你就需要考虑系统限制了
说明:当某一账号所能运行的线程跑满了之后,你就无法运行任何系统命令了,它会重试几次然后最终失败,这是系统线程跑满的一个最明显信号。
3. 关于系统参数说明
查看当前系统运行的总线程数量
top -H
# 也可以通过下面的命令查看
pstree -p | wc -l

3.1 PID_MAX
系统允许的最大PID值,也就是最多有多少个进程或者线程(因为一个线程也占用一个PID)。因为0-299是系统使用,所以最大值减去300就是用户可用的数量。这个值由系统自动生成的。最大可以是32767,但是其实新系统都没有这个限制,这个32767主要是为了和老版本的Linux和Unix兼容,如果你不考虑先后兼容问题,可以设置的更大,不过太大也没有什么意义。

这个值由2个数决定,其中一个是PIDS_PER_CPU_DEFAULT值为1024,另外一个是和当前服务器CPU个数有关。
这个PID是进程号,但是Linux中线程也占用一个号。如下图:

所以如果这个可用进程号范围太小也会出现无法创建线程的情况,不过一般不会因为这个原因。如何修改呢?
# 临时修改
echo NUMBER > /proc/sys/kernel/pid_max # 永久修改 修改该文件/etc/sysctl.conf,增加如下内容
kernel.pid_max=NUMBER
3.2 thread-max
系统允许的最大线程数量

这个值最主要受到物理内存限制,这个值是这么算出来的:
# mempages 是物理内存大小
# THREAD_SIZE 就是栈大小,通过ulimit -s可以看到,默认是8M
# PAGE_SIZE 内存页大小 通过 getconf PAGESIZE 查看
max_threads = mempages / ( * THREAD_SIZE / PAGE_SIZE)
3.3. Max user processes
最大用户进数量,也就是某个用户最多可以运行多少个进程或者线程

# CentOS 7是下面的文件
/etc/security/limits/d/20-nproc.conf
# CentOS 6是下面的文件
/etc/security/limits.d/90-nproc.conf

不同用户可以运行的线程数量也不同,就算你的内存很大,可用PID很多,系统允许的线程数量也非常多,但是你运行程序的账号如果被设置了很小的值也是不能超过这个值的。这个值可以计算:

这2个数应该很相近,但是我这里差别很大,是因为在/etc/security/limit.conf中设置了限制为16384,所以即便你在limit.d的20-nproc.conf针对某个用户设置了最大值也不生效,这两个文件如果设置了相同的内容则以最小值为准。看下图:

注意:ulimit -u NUMBER这个设置仅对当前shell有效,当你再打开一个shell时其值依然是之前的,以及你退出当前shell再次进入则又会恢复到之前的设置。
该文件的格式是:
USER TYPE RESOURCE NUMBER
USER:具体用户或者*表示所有用户
TYPE:soft表示当前系统生效的值、hard表示所能设置的最大值,soft不能大于hard的值,用“-”表示同时对soft和hard设置值
| RESOURCE | 说明 |
| core | 限定内核文件的大小 |
| data | 最大数据大小 |
| fsize | 最大文件大小 |
| memlock | 最大锁空间大小 |
| nofile | 单个进程最大打开文件数量 |
| rss | 最大持久设置 |
| statck | 最大栈大小 |
| cpu | 以分钟为单位的CPU时间 |
| nproc | 最大进程(含线程)数量 |
| as | 地址空间限制 |
| maxlogins | 某用户裕兴登录的最大条目 |
3.4 stack size
系统线程栈大小

这个是系统创建一个线程所消耗的内存大小,8M=8192kb。理论上最大线程数量应该小于物理内存除以8M(毕竟代码段、数据段也要占用内存以及管理线程的线程等)。如果你要想增大线程数量可以把栈大小调整小一点。
3.5 总结
一个JVM可以创建多少线程,首先由JVM设置决定(-Xms,-Xmx,-Xss),另外受到外部因素影响,就是系统设置(最大PID、最大线程、栈内存大小、最重要的还是物理内存由多少)、其二就是用户设置(用户可以运行多少个进程或线程),综合上述因素的最小值就是一个JVM可以创建多少线程。那么如何快速知道当前系统允许的最大进程或线程数量呢?
PID_MAX和thread_max这两个一般不用关心,很少有人去修改这个东西,而且就算改也会改大。另外要注意你当前的登录账户或者说运行程序的账户,这个就要去/etc/security/limit.conf和/etc/seurity/limits.d/20-nproc.conf里查看指定用户的最大进程数量也就是ulimit可以看到的,两个文件取最小值。如果最大进程数量不是问题,配置文件也没有问题,那你就要看看 /proc/sys/vm/max_map_count 这个值,如果也很大但是线程数量还是上不去你就要考虑是不是内存太小导致的。
4. 排查过程中使用到的命令
# 找到最消耗资源的线程
top –Hp `pgrep –u work java`
# 查看最消耗资源的线程在干什么
jstack -l PID > /tmp/a.txt
# 查看进程中线程的情况
top –H PID
# 查看该进程里面有多少线程
top –H pid | wc –l
# 查看系统目前运行的线程或进程总数
pstree –p | wc –l
java.lang.OutOfMemoryError: unable to create new native thread问题排查以及当前系统最大进程数量的更多相关文章
- java.lang.OutOfMemoryError: unable to create new native thread如何解决
工作中碰到过这个问题好几次了,觉得有必要总结一下,所以有了这篇文章,这篇文章分为三个部分:认识问题.分析问题.解决问题. 一.认识问题: 首先我们通过下面这个 测试程序 来认识这个问题:运行的环境 ( ...
- JVM内存越多,能创建的线程越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread。
一.认识问题: 首先我们通过下面这个 测试程序 来认识这个问题:运行的环境 (有必要说明一下,不同环境会有不同的结果):32位 Windows XP,Sun JDK 1.6.0_18, eclipse ...
- spark java.lang.OutOfMemoryError: unable to create new native thread
最近迁移集群,在hadoop-2.8.4 的yarn上跑 spark 程序 报了以下错误 java.lang.OutOfMemoryError: unable to create new native ...
- 剥下“java.lang.OutOfMemoryError: unable to create new native thread”的外衣 创建线程数公式(MaxProcessMemory - JVMMemory – ReservedOsMemory)
剥下“java.lang.OutOfMemoryError: unable to create new native thread”的外衣 星期一早上到了公司,据称产品环境抛出了最可爱的异常—OutO ...
- 解决java.lang.OutOfMemoryError: unable to create new native thread问题
解决:1.升级JVM到最新的版本 最新版本的JVM一般在内存优化方面做的更好,升级JVM到最新的版本可能会缓解测问题2.从操作系统层面去解决 使用64位操作系统 如果使用32位操作系统遇到unable ...
- 关于“java.lang.OutOfMemoryError : unable to create new native Thread”的报错问题
好吧 我发誓这是postgresql的Mirroring Controller的RT测试的最后一个坑了. 在这个RT测试的最后,要求测试Mirroring Controller功能在长时间运行下的稳定 ...
- 记一次内存溢出java.lang.OutOfMemoryError: unable to create new native thread
一.问题: 春节将至,系统访问量进入高峰期.随之系统出现了异常:java.lang.OutOfMemoryError: unable to create new native thread.在解决这个 ...
- [java] [error] java.lang.OutOfMemoryError: unable to create new native thread
前言 最近公司的服务器出现了oom的报错,经过一番排查,终于找到了原因.写下这篇博客是为了记录下查找的过程,也是为了帮助那些跟我门遇到的情况相同的人可以更快的寻找到答案. 环境 系统:linux(ce ...
- spark大批量读取Hbase时出现java.lang.OutOfMemoryError: unable to create new native thread
这个问题我去网上搜索了一下,发现了很多的解决方案都是增加的nproc数量,即用户最大线程数的数量,但我修改了并没有解决问题,最终是通过修改hadoop集群的最大线程数解决问题的. 并且网络上的回答多数 ...
随机推荐
- block学习二:使用Block替代回调
使用Block替代回调,分为三步进行:
- Exp5 MSF基础应用 20164302 王一帆
1.实验内容 1.1实验要求 一个主动攻击实践,ms08_067; 一个针对浏览器的攻击,MS10-018(成功且唯一),MS12-004(成功且唯一): 一个针对客户端的攻击,adobe_toolb ...
- go http
先看一个简单的 tcp 连接: // server ln, err := net.Listen("tcp", ":8000") if err != nil {} ...
- PHP序列号生成函数和字符串替换函数代码
/** * 序列号生成器 */ function snMaker($pre = '') { $date = date('Ymd'); $rand = rand(1000000,9999999); $t ...
- 解决Idea GitLab Clone failed: Authentication failed for的问题
刚使用GitLab做项目管理,在idea-check versionControl中使用git clone工程,一直报Clone failed: Authentication failed for ‘ ...
- C# datatable 重新排序
DataTable table = distributionManageService.Tb_fund_withdrawaGetPageList(pagination, queryJson);//设置 ...
- rest_framework之认证源码剖析
如果我们写API有人能访问,有人不能访问,则需要些认证. 如何知道该用户是否已登入? 如果用户登入成功,则给用户一个随机字符串,去访问另一个页面. 以前写session的时候,都是把session写c ...
- codeforces_Codeforces Round #541 (Div. 2)_abc
A. Sea Battle time limit per test 1 second memory limit per test 256 megabytes input standard input ...
- Linux shell编程-退出的状态码
linux 提供了一个专门的变量$?来保存上个已执行命令的状态码 linux 的错误状态退出状态码没有什么标准可遵循,但有一些参考 状态码 描述 0 命令成功结束 1 一般性未知错误 2 不适合的sh ...
- 【RL-TCPnet网络教程】第5章 PHY芯片和STM32的MAC基础知识
第5章 PHY芯片和STM32的MAC基础知识 本章节为大家讲解STM32自带的MAC和PHY芯片的基础知识,为下一章底层驱动的讲解做一个铺垫. 5.1 初学者重要提示 5.2 ...