添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

因为 cpu 使用率是时间段内的统计值, jstack 是一个瞬时堆栈只记录瞬时状态,两个根本不是一个维度的事,如果完全按照上面那一套步骤做的话碰到这种情况就傻眼了,冥思苦想半天却不得其解,根本不明白为什么这种代码会导致高 cpu

针对可能出现的这种情况,实际排查问题的时候 jstack 建议打印5次至少3次,根据多次的堆栈内容,再结合相关代码进行分析,定位高 cpu 出现的原因,高 cpu 可能是代码段中某个bug导致的而不是堆栈打印出来的那几行导致的。

2.1.1.1、 top :命令确定内存情况,找到CPU过高的Java程序

1、 top :命令

[work@vm10-123-3-2 ~]$ ps -mp 30030 -o THREAD,tid,time 
USER     %CPU PRI SCNT WCHAN  USER SYSTEM   TID     TIME
work     95.9   -    - -         -      -     - 23-10:29:53
work      0.0  19    - futex_    -      - 30030 00:00:00
work      0.0  19    - futex_    -      - 30045 00:00:47
work      0.0  19    - futex_    -      - 30046 00:00:20
work      0.0  19    - futex_    -      - 30047 00:00:20
work      0.0  19    - futex_    -      - 30048 00:00:20
work     95.6  19    - -         -      - 30332 23-08:37:00
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND               30332 work      20   0 10.338g 1.034g   5228 R 99.9  3.3  34523:57 java                   30206 work      20   0 10.338g 1.034g   5228 S  0.3  3.3  54:22.94 java                   31131 work      20   0 10.338g 1.034g   5228 S  0.3  3.3  12:37.95 java                   30030 work      20   0 10.338g 1.034g   5228 S  0.0  3.3   0:00.00 java                   30045 work      20   0 10.338g 1.034g   5228 S  0.0  3.3   0:48.52 java                   30046 work      20   0 10.338g 1.034g   5228 S  0.0  3.3   0:20.82 java                   30047 work      20   0 10.338g 1.034g   5228 S  0.0  3.3   0:20.83 java  
[work@vm10-123-3-2 bin]$ ./jstack 30030 | grep   767c   -A 10
"ObjectCleanerThread" #85 daemon prio=1 os_prio=0 tid=0x00007f20e290c800 nid=0x767c runnable [0x00007f20ec5a6000]
   java.lang.Thread.State: RUNNABLE
        at io.netty.util.Recycler$WeakOrderQueue$Head.run(Recycler.java:264)
        at io.netty.util.internal.ObjectCleaner$AutomaticCleanerReference.cleanup(ObjectCleaner.java:143)
        at io.netty.util.internal.ObjectCleaner$1.run(ObjectCleaner.java:62)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.lang.Thread.run(Thread.java:748)
"Log4j2-TF-7-AsyncLoggerConfig--4" #81 daemon prio=5 os_prio=0 tid=0x00007f20e087e800 nid=0x75fe runnable [0x00007f20ec4a5000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
[work@vm10-123-3-2 bin]$ 
  

我们可以使用命令 cat jstack.log | grep "java.lang.Thread.State" | sort -nr | uniq -c 来对 jstack 的状态有一个整体的把握,如果 WAITING 之类的特别多,那么多半是有问题啦。

3、内存问题排查

Java 的内存管理就是对象的分配和释放问题。

在 Java 中,内存的分配是由程序完成的,而内存的释放是由 GC 完成的,这种收支两条线的方法简化了程序员的工作,但同时也加重了JVM的工作。

Java 程序内存问题排查起来相对比 CPU 麻烦一些,场景也比较多。主要包括内存泄露、内存溢出、GC等问题。

3.1、内存泄露

3.1.1、定义

Java 内存泄漏就是没有及时清理内存垃圾,导致系统无法再给你提供内存资源(内存资源耗尽)。Java 内存泄露是说程序逻辑问题,造成申请的内存无法释放.这样的话无论多少内存,早晚都会被占用光的.最简单的例子就是死循环了.由于程序判断错误导经常发生此事。

程序运行过程中动态分配内存给一些临时对象,这些对象有下面两个特点

(1)这些对象是可达的

(2)这些对象是无用的,这些对象不会被GC回收,然而它却占用内存,造成内存浪费。简单的描述为“被分配的对象可达但已无用”。

3.1.2、根本原因

长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景

比如,A 对象引用B对象,A 对象的生命周期(t1 - t4 )比 B 对象的生命周期(t2 - t3)长的多。当B对象没有被应用程序使用之后,A对象仍然在引用着B对象。这样,垃圾回收器就没办法将B对象从内存中移除,从而导致内存问题,

内存泄漏大多是代码层面设计不严谨,或设计错误(编程错误),导致程序未能释放已不再使用的内存。

内存泄漏具有隐蔽性,积累性的特征,属于遗漏型缺陷,而不是过错型缺陷,不会直接产生可观察的错误症状,而是逐渐堆积,降低应用的整体性能。

可能有以下原因:

1、ThreadLocal

2、一个类含有静态变量,这个类的对象就无法被回收

3、内存中数据量太大,比如一次性从数据库中取出来太多数据

4、代码中存在死循环,或者循环过多,产生过多的重复的对象

5、各种连接没有关闭:例如数据库连接、网络连接

6、单例模式:如果单例对象持有外部对象的引用,那么外部对象将不会被回收,引起内存泄漏

7、静态集合类中对对象的引用,在使用完后未清空(只把对象设为null,而不是从集合中移除),使JVM不能回收,即内存泄漏

8、静态方法中只能使用全局静态变量,而如果静态变量又持有静态方法传入的参数对象的引用,会引起内存泄漏

3.1.4、症状

1、应用程序长时间连续运行时性能严重下降

2、CPU 使用率飙升,甚至到 100%

3、频繁 Full GC

4、应用程序抛出 OutOfMemoryError 错误

3.1.5、排查方法

内存泄露,很难觉察,需要不断的去观察 gc 的情况,然后 dump 内存进行分析,才能定位到哪一段代码有问题

3.1.5.1、去监控平台看GC的情况

3.1.5.2、查看服务器本地日志是否有 OutOfMemoryError 异常日志

3.1.5.3、将jvm内存存活的对象dump下来进行分析(有专门的平台)

3.2、内存溢出

3.2.1、定义

程序运行过程中无法申请到足够的内存(某一块内存空间块耗尽)而导致的一种错误。

3.2.2、 症状

1、Caused by:java.lang.OutOfMemoryError: Java heap space:堆内存溢出

2、Caused by: java.lang.OutOfMemoryError: MetaSpace:元空间内存溢出(存在于JDK 8中)

3、Caused by: OutOfMemoryError:unable to create new native thread:创建本地线程内存溢出

4、Caused by:java.lang.OutOfMemoryError: Direct buffer memory:直接内存溢出

5、Caused by: java.lang.StackOverflowError:栈内存溢出

3.2.3、排查方法

3.3、GC问题

3.1.1、Young GC频率高

原因:JVM 参数设置不合理 ,新生代空间大小设置不合理(过大:Young GC时间长,过小:Young GC频繁)

解决方案:

1、查看-Xmn-XX:SurvivorRatio等参数设置是否合理,能否通过调整jvm参数到达目的;

2、如果参数正常,但是young gc频率还是太高,需要分析 HeapDump 文件,看业务代码是否生成的对象是否合理。

3.1.3、Full GC 单次时间长 / Full GC 频率高

1、JVM参数设置不合理,尤其是新生代、老年代大小设置不合理,晋升阈值等

2、大对象创建,一次加载了过多数据到内存中(比如SQL查询未做分页),导致大对象进入了老年代

3、内存泄漏,大量对象引用没有释放

4、程序BUG、显示调用system.gc()等,触发Full GC

1、检查新生代大小、晋升阈值是否过小,导致大量生命周期较短的对象进入老年代

2、检查老年代是否过小,导致内存不足承载存活的生命周期较长的对象

3、分析 HeapDump 文件,看业务代码生成的对象是否合理

4.1、机器指标

4.1.1、icmp.ping.alive

icmp.ping.alive

⬤ 值为1:表示机器可以 ping通,

⬤ 值为0:表示机器不能 ping 通(异常状态);无法 ping 通时,可以理解为机器宕机。

4.1.2、cpu.idle

cpu.idlecpu 空闲百分比,该值越大代表系统 cpu 空闲。持续的低于 10%,则需要关注**

4.1.2.1、cpu.idle可能性原因:

1、大批量的计算,大批量读写数据库

2、批量操作问题,因为IO导致cpu报警

3、QPS远大于当前负载

4、数据库慢SQL、缓存大 key 也会导致此现象

5、宿主机问题(如果多节点“流量”均衡,单独机器不定期CPU报警,堆栈内存无异常,则可怀疑此现象,直接置换即可)

4.1.2.1、解决

3.1.5 内存泄露排查方案

4.2、GC 指标

4.2.1、指标

4.2.1.1、JVM GC 相关指标

jvm.fullgc.count 年老代GC的次数 ,包括的GC算法(MarkSweepCompact,PS MarkSweep,ConcurrentMarkSweep,G1 Old Generation) jvm.fullgc.time 年老代GC的时间,单位毫秒