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问题排查以及当前系统最大进程数量的更多相关文章

  1. java.lang.OutOfMemoryError: unable to create new native thread如何解决

    工作中碰到过这个问题好几次了,觉得有必要总结一下,所以有了这篇文章,这篇文章分为三个部分:认识问题.分析问题.解决问题. 一.认识问题: 首先我们通过下面这个 测试程序 来认识这个问题:运行的环境 ( ...

  2. JVM内存越多,能创建的线程越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread。

    一.认识问题: 首先我们通过下面这个 测试程序 来认识这个问题:运行的环境 (有必要说明一下,不同环境会有不同的结果):32位 Windows XP,Sun JDK 1.6.0_18, eclipse ...

  3. spark java.lang.OutOfMemoryError: unable to create new native thread

    最近迁移集群,在hadoop-2.8.4 的yarn上跑 spark 程序 报了以下错误 java.lang.OutOfMemoryError: unable to create new native ...

  4. 剥下“java.lang.OutOfMemoryError: unable to create new native thread”的外衣 创建线程数公式(MaxProcessMemory - JVMMemory – ReservedOsMemory)

    剥下“java.lang.OutOfMemoryError: unable to create new native thread”的外衣 星期一早上到了公司,据称产品环境抛出了最可爱的异常—OutO ...

  5. 解决java.lang.OutOfMemoryError: unable to create new native thread问题

    解决:1.升级JVM到最新的版本 最新版本的JVM一般在内存优化方面做的更好,升级JVM到最新的版本可能会缓解测问题2.从操作系统层面去解决 使用64位操作系统 如果使用32位操作系统遇到unable ...

  6. 关于“java.lang.OutOfMemoryError : unable to create new native Thread”的报错问题

    好吧 我发誓这是postgresql的Mirroring Controller的RT测试的最后一个坑了. 在这个RT测试的最后,要求测试Mirroring Controller功能在长时间运行下的稳定 ...

  7. 记一次内存溢出java.lang.OutOfMemoryError: unable to create new native thread

    一.问题: 春节将至,系统访问量进入高峰期.随之系统出现了异常:java.lang.OutOfMemoryError: unable to create new native thread.在解决这个 ...

  8. [java] [error] java.lang.OutOfMemoryError: unable to create new native thread

    前言 最近公司的服务器出现了oom的报错,经过一番排查,终于找到了原因.写下这篇博客是为了记录下查找的过程,也是为了帮助那些跟我门遇到的情况相同的人可以更快的寻找到答案. 环境 系统:linux(ce ...

  9. spark大批量读取Hbase时出现java.lang.OutOfMemoryError: unable to create new native thread

    这个问题我去网上搜索了一下,发现了很多的解决方案都是增加的nproc数量,即用户最大线程数的数量,但我修改了并没有解决问题,最终是通过修改hadoop集群的最大线程数解决问题的. 并且网络上的回答多数 ...

随机推荐

  1. 动态规划——Best Time to Buy and Sell Stock IV

    这是这个系列题目的第四个,题目大意和之前的差不多,但是这次提供最多k次的操作,操作还是不能同时操作即必须结束前一个操作才能进行后一个操作. 状态比较好理解,就是题目要求的缩小版,dp[k][i]表示进 ...

  2. Python+Tkinter 实现计算器功能

    #=================================================================================== import tkinter ...

  3. Java的四种引用——强引用、软引用、弱引用、虚引用

    目录 强引用 软引用 弱引用 虚引用 强引用 拥有强引用的对象永远不会被GC,可以根据引用的get方法获取到被引用对象 软引用 在内存充足的额时候,拥有软引用的对象不会被GC:即将内存溢出的时候,会对 ...

  4. date函数的属性

    date () a: "am"或是"pm" A: "AM"或是"PM" d: 几日,两位数字,若不足则补零:从" ...

  5. npm、cnpm、yarn 安装删除异同

    背景 一直觉得npm.cnpm.yarn的安装删除基本一样用哪个都行,不过俗话说的好,实践出真知,这里记录一下今天简单测试得到的结果总结. 可能会有错误,希望大家评论指正,十分感谢. 测试电脑系统:M ...

  6. java.lang.ClassCastException: net.sf.json.JSONNull cannot be cast to net.sf.json.JSONObject的解决方法

    报错情况已经说明了,在百度查了好几个解决方法,这里总结一下: 首先:加一个判断是否为空,再做操作 // 得到json串 String jsonString = UtilPOSTGET.UPost(FO ...

  7. 使用Jacksum对文件夹和文件生成checksum

    Jacksum 是一个java开源工具, 用来 给单个文件生成checksum, 也可以给整个文件中所有文件生成checksum,生产的checksum 可以是MD系列,也可sha. 你可以参考​ 官 ...

  8. 【原创】.Net WebForm Calendar 日历控件常用方法

    微软官方地址 https://msdn.microsoft.com/en-us/library/add3s294.aspx 1.设置日历控件单个日期Table Cell样式 颜色/外观/边距 prot ...

  9. 微信测试号开发入门配置问题java

    这个测试号配置弄了好几天了,入了无数坑,终于解决了...辛苦我了,手动安慰一下自己.. 为了萌新们以后不要再浪费时间绕半天做无用功.看看楼楼的艰苦历程吧. 此教程针对没有云服务器,没有自己的域名的.没 ...

  10. Cmd命令 查看端口被占用

    1)第一步 打开cmd命令窗口,输入命令:netstat -ano|findstr 输入端口号 2)第二步 继续输入命令:tasklist|findstr  第一步查询到的进程号 3)第三步 根据第二 ...