先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前在阿里
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
-
内存 - ok
-
CPU 利用率 - ok
-
CPU 负载 - ok
也没什么异常,CPU 利用率和负载啥的都很低……到底问题出在哪?
这么一点点的看指标太费劲了,还是拿 Arthas 看吧,JVM 相关的指标它基本都显示,比较全:
[arthas@20727]$ dashboard
Memory used total max usage GC
heap 498M 1963M 1963M 25.1% gc.ps_scavenge.count 3
ps_eden_space 98M 512M 512M 19.5% gc.ps_scavenge.time(ms) 44
ps_survivor_space 0K 87040K 87040K 0.00% gc.ps_marksweep.count 1
ps_old_gen 39M 1365M 1365M 2.88% gc.ps_marksweep.time(ms) 87
nonheap 32M 180M -1 17.7%
code_cache 10M 50M 240M 20%
metaspace 20M 128M -1 15.6%
compressed_class_space 2M 2M 1024M 0.25%
看起来JVM 级别的内存也没啥问题,再看看线程呢:
[arthas@20727]$ thread
-
Threads Total: 9831, NEW: 0, RUNNABLE: 8, BLOCKED: 0, WAITING: 9789, TIMED_WAITING: 2, TERMINATED: 0, Internal threads
- 17
wc,这什么玩意!快 1w 个线程!还基本上都是 WAITING!
赶紧看看这些 WAITING 的线程都是些啥:
ead --state WAITING
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIM TIME INTERRUPT DAEMON
此处省略 9000+ 行……
9822 pool-9813-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9823 pool-9814-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9824 pool-9815-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9825 pool-9816-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9826 pool-9817-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9827 pool-9818-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9828 pool-9819-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9829 pool-9820-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9830 pool-9821-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9831 pool-9822-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9832 pool-9823-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9833 pool-9824-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9834 pool-9825-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9835 pool-9826-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9836 pool-9827-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9837 pool-9828-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9838 pool-9829-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9839 pool-9830-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
9840 pool-9831-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
看到这个线程名,我也大概明白了,一定是哪个小天才在代码里下毒。
从线程名称来看,肯定是线程池里的线程嘛,而且是默认的线程名生成规则。线程池里的线程都是通过 ThreadFactory 来创建的,而默认的 ThreadFactory 生成规则就是这样:
DefaultThreadFactory() {
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)
先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前在阿里
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
图片转存中…(img-pUn7A7vc-1715292913247)]
[外链图片转存中…(img-9dHz3ERm-1715292913248)]
[外链图片转存中…(img-9MS6prsp-1715292913248)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新