SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
// 前缀,每一个新的 ThreadFactory 都有一个新的前缀
// 每一个线程池,都只有一个 ThreadFactory
namePrefix = “pool-” +
poolNumber.getAndIncrement() +
“-thread-”;
}
public Thread newThread(Runnable r) {
// 每个线程都会使用 factory的前缀,然后加上自增的线程数
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
所以这个问题,肯定是哪个小天才,在代码里每次都新建线程池,然后还不 shutdown 导致的!随便找个线程,看看它的 stack:
“pool-1-thread-1” Id=10 WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@6ba7592
at sun.misc.Unsafe.park(Native Method)
- waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@6ba7592
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
实锤了,就是每次 new 线程池不 shutdown……现在只需要找到创建线程池的地方,再翻翻提交记录,就可以知道是哪个小天才了……
但代码这么多,想找到这个新建线程池的地方还是有点麻烦的,搜代码可不太方便。这里还是用 Arthas 来看看,stack 一看就知道
不过如果这里直接 stack ThreadPoolExecutor#execute 方法的话,干扰信息可能太多了,毕竟调用 execute 的地方太多,不好抓到重点
所以还是 stack 线程池初始化的方法比较好:
[arthas@20727]$ stack java.util.concurrent.ThreadPoolExecutor
Affect(class count: 0 , method count: 0) cost in 4 ms, listenerId: 5
No class or method is affected, try:
-
Execute
sm CLASS_NAME METHOD_NAME
to make sure the method you are tracing actually exists (it might be in your parent class). -
Execute
options unsafe true
, if you want to enhance the classes under thejava.*
package. -
Execute
reset CLASS_NAME
and try again, your method body might be too large. -
Check arthas log: /home/jiangxin/logs/arthas/arthas.log
-
Visit https://github.com/alibaba/arthas/issues/47 for more details.
这……是不支持吗?
Arthas 的增强策略是比较保守的,部分系统级别的类它不做增强,java.* 包下的类默认是不增强的,需要手动开启才行:
[arthas@20727]$ options unsafe true
NAME BEFORE-VALUE AFTER-VALUE
unsafe false true
再次执行 stack 之后,可以用了。过了两分钟之后,终于有一次调用:
ts=2021-06-12 12:04:03;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@4e0e2f2a
@java.util.concurrent.ThreadPoolExecutor.()
at java.util.concurrent.Executors.newFixedThreadPool(Executors.java:89)
at XXXService.sendSms(XXXService.java:782)
//…
终于找到了这个骚操作代码……它是这么写的:
ExecutorService executorService = Executors.newFixedThreadPool(8);
executorService.submit(smsTask);
//…
和我猜测的一样,果然是每次 new,然后还不 shutdown。
这也能和上面的情况对上了,多出的内存占用是因为这小一万个线程……而且这些线程池没有任务需要执行的话,线程肯定是 WAITING 状态,也不会占用 CPU 的利用率,负载有不会有影响。不仔细还真看不出问题 ,还是得结合各种指标来看,综合分析。
解决这个问题倒简单,让写这个屎代码的人去改了,然后拉出去祭天。
可是运维大哥那边……已经装出去了,这下脸可是丢完了。上次好不容易挣回来的面子,这次啥都没了
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Linux运维工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Linux运维知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip1024b (备注Linux运维获取)
习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Linux运维知识点,真正体系化!**
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip1024b (备注Linux运维获取)
[外链图片转存中…(img-0rOCC8WN-1712654044684)]