SocketInputStream.socketRead0() 中 CPU 使用率高的原因

2022-09-02 22:44:32

在分析本土Web应用程序时,我遇到了非常奇怪的(至少对我来说)观察。

几乎所有的时间都花在一个类的方法上。这并不奇怪,因为我的应用程序在每个请求上都与远程服务进行联网。奇怪的是,不仅挂钟时间使用率很高,CPU时钟时间也很高。我不明白为什么CPU时间很长,因为如果我的应用程序等待远程服务回复(实际上并不那么快),那么应用程序本身就无能为力了。因此,CPU 时间应该明显较低。socketRead0()SocketInputStream

更多观察:

  • 采样模式下的VisualVM显示该方法消耗了高达95%的时间(挂钟时间和CPU时间);SocketInputStream.socketRead0()
  • mpstat(我们使用Linux作为操作系统)显示大约90%的用户时间和大约1-3%的系统时间(其余的是空闲时间);
  • 部署在专用服务器上的应用程序;
  • 远程服务也是HTTP Web应用程序。平均响应时间约为100ms。平均响应大小约为 2Kb。
  • 我的应用程序使用弹簧与远程服务交互,而不是直接交互。RestTemplateSocketInputStream

现在我只有一个想法 - 也许这是在JVM中调用本机方法的开销(是本机的)?SocketInputStream.socketRead0()

你觉得怎么样?这还有其他原因吗?


答案 1

我面临着同样的问题。我的应用程序有一个非常高的qps,每个请求都会让我发送多个节俭调用,这些调用使用此本机api:socketRead0

所以我决定做一个实验。我制作了一个模拟服务器,其中api在返回前睡眠30秒,客户端调用此api。我的目的是在网络io发生时测试线程状态。根据我的线程转储,线程状态为 。RUNNABLE

这解释了两件事:

  1. 具有高 qps 阻塞 io 的应用程序将面临高 CPU 负载值

  2. 您的java线程仍在jvm中运行,因为线程状态将导致高用户空间CPU利用率RUNNABLE

这两者都会让你的CPU忙碌。

我在实验过程中注意到,系统空间CPU利用率很低。我认为这与jvm和os之间的线程调度策略差异有关。我们知道热点线程模型是1:1,这意味着一个jvm线程对一个os线程。当阻塞io发生时,例如内核线程将设置为状态并且不会阻塞cpu,但用户空间线程正在阻塞(等待)。当这种情况发生时,我认为我们需要重新思考应用程序中的基本I / O模型。socketRead0S


答案 2

VisualVM 不是将负载显示为绝对值,而是显示为相对值,因此它只是意味着您的应用程序不再有任何 CPU 消耗点。

我认为您应该将 VisualVM 配置为不深入挖掘,而是将此方法调用视为代码(或 spring)中方法的一部分。

我已经经历过这样的行为,但它看起来不需要任何优化。Web应用程序只需要从套接字(即HTTP请求,数据库,内部网络服务等)读取数据,并且没有帮助。


推荐