首页 > 系统相关 >Java进程内线程数量限制的相关学习

Java进程内线程数量限制的相关学习

时间:2023-12-11 13:44:56浏览次数:34  
标签:kernel Java max PID 内线 线程 进程 限制 CPU

Java进程内线程数量限制的相关学习


背景

还是之前出现 cannot create native thread 的问题的后续
周末在家学习了下如何在容器外抓取dump.
也验证了下能否开启超过宿主机 nofile 配置的进程数量. 

想着总结一下学习到的东西, 不枉周六不午休, 周天晚上还开会到11点多. 

关于线程

Linux 里面进程是资源分配的最小单元
      现成是字段调度的最小单元. 

实际上Linux并没有实现严格意义上的进程与线程的关系.
他实际上是一个LWP 轻量级进程的 概念来实现的线程. 

java启动之后 会不停地创建一些线程来干具体的工作. 
有一些java自身使用, 有一些事具体业务使用的. 
这里想着从系统层和java层进行一些简单的学习. 

Java的线程信息

Java 启动之后其实会产生很多现成
1. Java进程号对应的线程.  这个应该是一个守护或者是单独的现成
2. 会有CPU个数的GC线程.存在. 这个与GC的算法有关系.
3. 会有一些compile的进程, 用于进行解释和编译. 而且还分为C1和C2的编译器.
4. 会有连接redis的线程, 连接数据库的线程, 以及一些logback,记录日志等的线程. 
5. 计划任务的线程, 如果有的话.
6. 还有一个核心线程 比如 http 开头 tomcat 或者是 jetty的线程池

一般情况下: 这个http类似的核心线程池的数量一般是比较重要的内容. 

关于http线程池的配置

很早之前研究过这个线程池
当时的想法是线程池的数量跟能够承载的并发数正相关. 

跟CPU有关系, 跟网络有关系, 跟数据库有关系. 

CPU多了肯定能够支撑更多的线程数量. 
但是不完全有CPU数量来决定. 
理论上应该是
CPU数量/平均每个线程一次请求需要的时间
这个线程处理的时间.  是on CPU的时间.  要排除 网络,数据库,的请求时间. 
因为线程的主动和被动的的切换是很快的 是微秒级, 每秒钟进行万级别的上下切换. 

所以理论上一个CPU可以干很多事情, 这个可以类比 单线程的redis的请求处理过程. 

但是理论上不要将CPU消耗的太多
建议至少留有 40%的资源用于 网络,io,系统监控, 调度等资源
也就是建议是 
0.6*CPU数量/(平均每个线程一次请求需要的时间+调度损耗+切换损耗等)
保证机器不要太高的CPU导致问题. 

关于线程数量的限制

Linux下线程数量的限制参数很多
通过国内最好国际最差的百度搜索引擎可以查到如下内容: 

stack_size
max_user_processes
sys.vm.max_map_count
sys.kernel.threads-max
sys.kernel.pid_max

分别解释为

stack_size

stack_size 
是一个进程/线程重建之后建立的栈区域大小
如果值太大, 那么系统支持的栈数量就会很小, 
如果值太小, 则很容易出现栈溢出的问题,导致功能不正常.
理论上机器上面能够用来存储栈的内存大小 除以 stack_size 就是可以创建进程的数量了
这个值是一个硬件限制的值, 还可能会受到其他参数的影响. 

max_user_processes

max_user_processes

可以通过 ulimit -a 或者是
ulimit -u 进行查看 可以看到一个用户级别的能够打开多少个线程信息

这个值可以你在 /etc/security/limits.conf 里面进行限制. 
需要注意一个配置文件的优先级:
专有的比全部的级别要高.
/etc/security/limits.d/
的优先级高于
/etc/security/limits.conf

sys.vm.max_map_count

max_map_count
会限制一个进程可以拥有的VMA(虚拟内存区域)的数量
这个参数也会间接影响进程能够创建的线程数量. 

经常遇到的问题是:
报错“max virtual memory areas vm.max_map_count [65530] is too low, 
increase to at least [262144]”

修改方式:
sysctl -w vm.max_map_count=262144
永久修改为:
vim /etc/sysctl.conf
vm.max_map_count=262144

sys.kernel.threads-max

该参数大致意思是,系统内核fork()允许创建的最大线程数,
在内核初始化时已经设定了此值,但是即使设定了该值,但是线程结构只能占用可用RAM page的一部分,
约1/8(注意是可用内存,即Available memory page),如果超出此值1/8则threads-max的值会减少

内核初始化时,默认指定最小值为MIN_THREADS = 20,MAX_THREADS的最大边界值是由FUTEX_TID_MASK值而约束,
但是在内核初始化时,kernel.threads-max的值是根据系统实际的物理内存计算出来的

sys.kernel.pid_max

kernel允许当前系统分配的最大PID identify,
如果kernel 在fork时hit到这个值时,
kernel会wrap back到内核定义的minimum PID identify,
意思就是不能分配大于该参数设定的值+1,该参数边界范围是全局的,属于系统全局边界

参数范围

参数名称 范围边界
kernel.pid_max 系统全局限制
kernel.threads-max 系统全局限制
vm.max_map_count 进程级别限制
/etc/security/limits.conf 用户级别限制

关于K8S模式下的限制

PodPidsLimit 参数
Kubernetes 允许你限制 Pod 中运行的进程个数。
你可以在节点级别设置这一限制, 而不是为特定的 Pod 来将其设置为资源限制。
每个节点都可以有不同的 PID 限制设置。 
要设置限制值,你可以设置 kubelet 的命令行参数 --pod-max-pids,或
者在 kubelet 的配置文件中设置 PodPidsLimit

在某些 Linux 安装环境中,操作系统会将 PID 约束设置为一个较低的默认值,例如 32768。
这时可以考虑提升 /proc/sys/kernel/pid_max 的设置值。

如果资料来自官网, 需要注意如果K8S的kubelet 限制了 pid in pod
那么在达到这个limit 时 程序会提示无法 create native thread
这是在产品内部进行 执行命令时 比如 top ls 等
会提示  cannot fork 

这个地方很容易出现坑, 需要云平台进行协助设置. 

官方关于这个参数的解释

你可以配置 kubelet 限制给定 Pod 能够使用的 PID 个数。 
例如,如果你的节点上的宿主操作系统被设置为最多可使用 262144 个 PID, 
同时预期节点上会运行的 Pod 个数不会超过 250,
那么你可以为每个 Pod 设置 1000 个 PID 的预算,避免耗尽该节点上可用 PID 的总量。 

如果管理员系统像 CPU 或内存那样允许对 PID 进行过量分配(Overcommit),
他们也可以这样做, 只是会有一些额外的风险。
不管怎样,任何一个 Pod 都不可以将整个机器的运行状态破坏。 
这类资源限制有助于避免简单的派生炸弹(Fork Bomb)影响到整个集群的运行。

一个不是总结的总结

线程池,连接池都是为了减少资源创建和销毁需要时间开销的管理方式

但既然是一种管理, 他自就会有开销

重点在于. 管理的overhead和节约的overhead 之间的比率. 
如果能够在仅仅使用 1k个CPU的cycle 进行管理的开销下能够减少 1G甚至更高的CPU开销
那么这个管理就非常值得. 

所以线程池的使用 是要很慎重的
如果每次申请了线程不进行复用, 其实没有必要创建线程池. 
必须复用的东西才可以进行池化的处理. 

换句话说, 如果你想更高的承载客户的需求, 可以将线程池的max 设置的比较大
但是这样会导致每个人都会变慢. 

一个合理的方式是在 CPU 出去必然的开销之外, 可以用于业务处理的周期内. 
插空进去最多的线程数量, 就是最优秀的参数配置.

还是那句话
这个配置跟CPU的算力有关系,跟网络,跟IO,跟数据库,跟应用的业务逻辑有关系. 
并没有一个放之四海而皆准的公式来进行计算.  

标签:kernel,Java,max,PID,内线,线程,进程,限制,CPU
From: https://www.cnblogs.com/jinanxiaolaohu/p/17894189.html

相关文章

  • java类
    一、类1、类的定义2、对象的使用3、对象内存图 (1)单个对象内存图(2)多个对象内存图(3)多个对象指向相同3、成员变量和局部变量的区别 二、封装1、封装概述 2、private关键词 3、this关键词 三、构造方法1、概述 2、注意事项 ......
  • 学习教程大全(java、c#、Web、GIS、Andriod)
    学习教程大全(java、c#、Web、GIS、Andriod)1.Andriod││AndroidApp开发入门使用AndroidStudio2.X开发环境第2版.pdf││AndroidStudio开发实战:从零基础到App上线.pdf││Android4高级编程(jb51.net).pdf││Android程序员面试宝典pdf扫描版.pdf......
  • Java 环境搭建
    Java环境搭建Java本来是sun公司的,然后被Oracle收购了。因为Java本身也是开源产品,所以就出现了官方OracleJDK和开源的OpenJDK。这里一般搭建说的是OracleJDK~做Java开发的朋友跟我说,我们目前使用这个层面是无法感知两个版本的区别的,都能正常使用和学习。(我毕......
  • 我用 AI 写的《JavaScript 工程师的 Python 指南》电子书发布啦!
    关于本书你好,我是luckrnx09,一名靠React恰饭的前端工程师,很高兴向你介绍我的第一本开源电子书《JavaScript工程师的Python指南》。本书的内容完全免费,开源地址:https://github.com/luckrnx09/python-guide-for-javascript-engineers为什么会有这本书2022年,ChatGPT引起了......
  • java监听全局组合键
    1.jintellitypepom<!--不能注册多个组合键比如alt+abc--><!--https://mvnrepository.com/artifact/com.melloware/jintellitype--><dependency> <groupId>com.melloware</groupId> <artifactId>jintellitype</artifactId> <ver......
  • Java 面向对象入门
    第一章:类与对象1.1类与对象类与对象的定义和使用在Java中定义类,使用关键字class完成。语法如下:class类名称{属性(变量);行为(方法);}我们可以通过下面的实例来感受一下如何定义一个Person类。classPerson{//类名称首字母大写Stringn......
  • Js(Javascript)中的apply方法的使用
    ​ JavaScript中的apply()方法用于调用函数,允许指定函数的this对象和参数。也就是通过function的apply方法来调用方法,可以改变方法的this的对象,并且还可以传入方法参数,apply对于面向对象编程还是很有用的。参考文档:Js(Javascript)中的apply方法的使用-CJavaPy1、基本语......
  • 进程间通信-信号-pipe-fifo
    编译运行FifoConsumerProducer一起运行Testmfpipe文件夹PipeListarg理解()Pipe概念:管道是一种在两个进程之间进行通信的机制。个进程的输出可以通过管道传递给另一个进程的输入创建:在C语言中,可以使用pipe系统调用创建管道。管道有两端,一个用千写入,一个用干读......
  • java中C3P0、Druid、HikariCP 、DBCP连接池的jar包下载与IDEA配置
    ##一、什么是连接池连接池是应用程序与数据库之间的一个缓冲区,它存储了一定数量的空闲数据库连接,当应用程序需要连接数据库时,可以从连接池中获取一个可用连接,使用完毕后再将连接归还给连接池,从而避免了每次连接都需要创建和销毁连接的开销,提高了应用程序的性能和可伸缩性。连接池......
  • java 策略模式解决if-else ,函数式接口解决编写多个子类的问题
    /***@author:szc*@date:2023/9/222:45*@version:1.0*@description:从map中获取函数式接口,解决if-else多个子类问题*/@ServicepublicclassMapToInterface{@AutowiredprivateTypeResulttypeResult;privatestaticMap<String,Functio......