什么会导致Java应用程序的CPU使用率飙升

125次阅读

共计 3004 个字符,预计需要花费 8 分钟才能阅读完成。

问题

  • 无限循环的 while 会导致 CPU 使用率飙升吗?
  • 经常使用 Young GC 会导致 CPU 占用率飙升吗?
  • 具有大量线程的应用程序的 CPU 使用率是否较高?
  • CPU 使用率高的应用程序的线程数是多少?
  • 处于 BLOCKED 状态的线程会导致 CPU 使用率飙升吗?
  • 分时操作系统中的 CPU 是消耗 us 还是sy

思路

1. 如何计算 CPU 使用率?

CPU%= 1 – idleTime / sysTime * 100

  • idleTime:CPU 空闲的时间
  • sysTime:CPU 处于用户模式和内核模式的时间总和

2. 与 CPU 使用率有关的是什么?

人们常说,计算密集型程序的 CPU 密集程度更高。

那么,JAVA 应用程序中的哪些操作更加 CPU 密集?

以下列出了常见的 CPU 密集型操作:

  • 频繁的 GC; 如果访问量很高,可能会导致频繁的 GC 甚至 FGC。当调用量很大时,内存分配将如此之快以至于 GC 线程将连续执行,这将导致 CPU 飙升。
  • 序列化和反序列化。稍后将给出一个示例:当程序执行 xml 解析时,调用量会增加,从而导致 CPU 变满。
  • 序列化和反序列化;
  • 正则表达式。我遇到了正则表达式使 CPU 充满的情况; 原因可能是 Java 正则表达式使用的引擎实现是 NFA 自动机,它将在字符匹配期间执行回溯。我写了一篇文章“正则表达式中的隐藏陷阱”来详细解释原因。
  • 线程上下文切换; 有许多已启动的线程,这些线程的状态在 Blocked(锁定等待,IO 等待等)和 Running 之间发生变化。当锁争用激烈时,这种情况很容易发生。
  • 有些线程正在执行非阻塞操作,例如 while (true) 语句。如果在程序中计算需要很长时间,则可以使线程休眠。

3. CPU 是否与进程和线程相关?

现在,分时操作系统使用循环方式为进程调度分配时间片。如果进程正在等待或阻塞,那么它将不会使用 CPU 资源。线程称为轻量级进程,并共享进程资源。因此,线程调度在 CPU 中也是分时的。但在 Java 中,我们使用 JVM 进行线程调度。因此,通常,线程调度有两种模式:时间共享调度和抢占式调度。

答案

1. while 的无限循环会导致 CPU 使用率飙升吗?

是。

首先,无限循环将调用 CPU 寄存器进行计数,此操作将占用 CPU 资源。那么,如果线程始终处于无限循环状态,CPU 是否会切换线程?

除非操作系统时间片到期,否则无限循环不会放弃占用的 CPU 资源,并且无限循环将继续向系统请求时间片,直到系统没有空闲时间来执行任何其他操作。

stackoverflow 中也提出了这个问题:为什么无意的无限循环增加了 CPU 的使用?

https://stackoverflow.com/questions/2846165/why-does-an-infinite-loop-of-the-unintended-kind-increase-the-cpu-use

2. 频繁的 Young GC 会导致 CPU 占用率飙升吗?

是。

Young GC 本身就是 JVM 用于垃圾收集的操作,它需要计算内存和调用寄存器。因此,频繁的 Young GC 必须占用 CPU 资源。

让我们来看一个现实世界的案例。for 循环从数据库中查询数据集合,然后再次封装新的数据集合。如果内存不足以存储,JVM 将回收不再使用的数据。因此,如果所需的存储空间很大,您可能会收到 CPU 使用率警报。

3. 具有大量线程的应用程序的 CPU 使用率是否较高?

不时。

如果通过 jstack 检查系统线程状态时线程总数很大,但处于 Runnable 和 Running 状态的线程数不多,则 CPU 使用率不一定很高。

我遇到过这样一种情况:系统线程的数量是 1000+,其中超过 900 个线程处于 BLOCKED 和 WAITING 状态。该线程占用很少的 CPU。

但是大多数情况下,如果线程数很大,那么常见的原因是大量线程处于 BLOCKED 和 WAITING 状态。

4. 对于 CPU 占用率高的应用程序,线程数是否较大?

不是。

高 CPU 使用率的关键因素是计算密集型操作。如果一个线程中有大量计算,则 CPU 使用率也可能很高。这也是数据脚本任务需要在大规模集群上运行的原因。

5. 处于 BLOCKED 状态的线程是否会导致 CPU 占用率飙升?

不会。

CPU 使用率的飙升更多是由于上下文切换或过多的可运行状态线程。处于阻塞状态的线程不一定会导致 CPU 使用率上升。

6. 如果分时操作系统中 CPU 的值 ussy值很高,这意味着什么?

您可以使用命令查找 CPU 的值 ussytop,如以下示例所示:

  • us:用户空间占用 CPU 的百分比。简单来说,高我们是由程序引起的。通过分析线程堆栈很容易找到有问题的线程。
  • sy:内核空间占用 CPU 的百分比。当 sy 为高时,如果它是由程序引起的,那么它基本上是由于线程上下文切换。

经验

如何找出 CPU 使用率高的原因?下面简要描述分析过程。

如果发现应用程序服务器的 CPU 使用率很高,请首先检查线程数,JVM,系统负载等参数,然后使用这些参数来证明问题的原因。其次,使用 jstack 打印堆栈信息并使用工具分析线程使用情况(建议使用 fastThread,一个在线线程分析工具)。

以下是一个真实案例:

一天晚上,我突然收到一条消息,说 CPU 使用率达到了 100%。所以立即,我倾倒了用 jstack 打印的堆栈信息。

进一步检查日志:

onsumer_ODC_L_nn_jmq919_1543834242875 - priority:10 - threadid:0x00007fbf7011e000 - nativeid:0x2f093 - state:RUNNABLE
stackTrace:
java.lang.Thread.State:RUNNABLE
at java.lang.Object.hashCode(Native Method)
at java.util.HashMap.hash(HashMap.java:362)
at java.util.HashMap.getEntry(HashMap.java:462)
at java.util.HashMap.containsKey(HashMap.java:449)
at com.project.order.odc.util.XmlSerializableTool.deSerializeXML(XMLSerializableTool.java:100)
at com.project.plugin.service.message.resolver.impl.OrderFinishMessageResolver.parseMessage(OrderFinishMessageResolver.java:55)
at com.project.plugin.service.message.resolver.impl.OrderFinishMessageResolver.parseMessage(OrderFinishMessageResolver.java:21)
at com.project.plugin.service.message.resolver.impl.AbstractResolver.resolve(AbstractResolver.java:28)
at com.project.plugin.service.jmq.AbstractListener.onMessage(AbstractListener.java:44)

现在通过这个日志找到了问题:用于反序列化 MQ 消息实体的方法导致 CPU 使用率飙升。

正文完
 0