java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java频繁创建线程排查

Java频繁创建线程排查和解决方案

作者:赵广陆

文章讨论了Java线程池的使用和配置,以及线程对内存的影响,作者通过实验和理论分析,指出线程并不是占用JVM的内存,而是由操作系统分配的本地线程,文章还提到了线程池的优点,如节省系统开销、提高性能和方便控制

产生原因

因为编译工具突然报错,需要手动创建线程池,之前都是用ExecutorService直接创建的线程池用的封装好的,但是阿里巴巴规范不让用,网上找了个代码copy导致创建的线程池无法关闭,暂时没关注原因,解决的话还是使用ExecutorService的注入类,并且配置好线程池参数,而不去new线程池,也不要随缘关闭线程池,引发了一些列知识点总结一下。

Java 服务器可以跑多少个线程

一台Java服务器能跑多少个线程?这个问题来自一次线上报警如下图,超过了我们的配置1200阈值产生了预警。

[告警触发:1] thread_size Alerts Firing [critical] jvm当前活跃线程数超过1200, 当前值:1258, 描述: 当前活跃线程数已经超过1200 alertname:thread_size application:oldlu:gray exported_application:msun-ecg-app-ecg hospital:/人民 instance:10.9.2.155:30260 job:jvm_exporter notify:base r_cluster:eu0 severity:critical type:jvm 开始时间:2022-11-15 15:05:01

共计1258个线程,和监控数据得出的吻合。但这个数量应该是大了,我们都知道线程多了,就会有线程切换,带来性能开销。

当时就想到一台java服务器到底可以跑多少个线程呢?跟什么有关系?现整理如下。

每个线程都有一个线程栈空间通过-Xss设置,查了一下我们服务器的关于jvm内存的配置

-Xms4096m
-Xmx4096m
-XX:MaxPermSize=1024m1.2.3.

只有这三个,并没有-Xss 和-XX:ThreadStackSize的配置,因此是走的默认值。几种JVM的默认栈大小

可以通过如下命令打印输出默认值的大小,命令:jinfo -flag ThreadStackSize

[root@host-192-168-202-229 ~]#jinfo -flag ThreadStackSize 1807

-XX:ThreadStackSize=10241.2.3.

线程数量=(机器本身可用内存-JVM分配的堆内存)/Xss的值,比如我们的容器本身大小是8G,堆大小是4096M,走-Xss默认值,可以得出 最大线程数量:4096个。

根据计算公式,得出如下结论:

我们知道操作系统分配给每个进程的内存大小是有限制的,比如32位的Windows是2G。因此操作系统对一个进程下的线程数量是有限制的,不能无限的增多。经验值:3000-5000左右。

刚才说的是不考虑系统限制的情况,那如果考虑系统限制呢,主要跟以下几个参数有关系

线程是非常宝贵的资源,我们要严格控制线程的数量,象上面我们的截图情况,显然线程数量过多。这个是跟我们自己配置了fixed大小的线程池有关系。京东有自己的rpc框架jsf,里面可以针对每个服务端口设置线程大小。

Java 线程多影响内存吗

主要的影响如下:

而针对上述所描述的问题,解决的办法归根到底就是:重用已有的线程,从而减少线程的创建。所以这就涉及到线程池(ExecutorService)的概念了,线程池的基本作用就是进行线程的复用,下面将具体介绍线程池的使用

使用线程池管理线程的优点

优先级线程池的优点

从上面我们可以得知,创建一个优先级线程池非常有用,它可以在线程池中线程数量不足或系统资源紧张时,优先处理我们想要先处理的任务,而优先级低的则放到后面再处理,这极大改善了系统默认线程池以FIFO方式处理任务的不灵活

java线程占多大的内存,占哪里的内存

说到线程,我们往往想到的是线程安全、线程池,很少会去考虑线程的内存。

那么一个线程占用多大的内存?占用哪里的内存呢?

占多大的内存

占哪里的内存

这TM还用问?java线程当然是占用jvm的内存啊!

好,我们做个实验,用jMeter同时并发调用java里某个接口200次,让java里增加大约190个线程(tomcat会有驻留线程,我这是10个),看下堆内存的情况:

从图中看到当线程猛增时,堆内存也猛增,然后堆内存会迅速下降,这是因为堆上new了大量的对象,所以猛增,然后线程执行完后,对象被GC了,所以下降。

上面提到堆内存下降是因为线程执行完了,GC回收了new出来的对象。但从图中看出,堆内存下降后线程数并没有下降,这是为什么呢?

用过线程池的都知道,线程执行完后并不会立即销毁掉,会有一个保活时间,保活时间过了后才会销毁 jdk1.8,每个线程占用1M内存,如果是占用的堆内存,那堆内存应该会增加190M左右,但从图中看并没有,所以线程不是占用的堆内存空间。

实际上,java里每新起一个线程,jvm会向操作系统请求新起一个本地线程,此时操作系统会用空闲的内存空间来分配这个线程。所以java里线程并不会占用 jvm的内存空间,而是会占用操作系统空闲的内存空间,所以不会引起oom,占用的是堆外内存

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文