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

Posted

技术标签:

【中文标题】SocketInputStream.socketRead0() 中 CPU 使用率高的原因【英文标题】:Reasons for high CPU usage in SocketInputStream.socketRead0() 【发布时间】:2012-09-19 05:50:02 【问题描述】:

在分析本土网络应用程序时,我遇到了非常奇怪的(至少对我而言)观察结果。

几乎所有时间都花在SocketInputStream 类的socketRead0() 方法上。这并不奇怪,因为我的应用程序对每个请求都使用远程服务进行联网。奇怪的是,这种方法不仅 wall clock time 使用率很高,CPU clock time 也非常高。我不明白为什么 CPU 时间很高,因为如果我的应用程序等待远程服务回复(实际上这并没有那么快),那么应用程序本身就没有什么可做的了。所以 CPU 时间应该很短。

更多观察:

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

目前我只有一个想法——也许这是在 JVM 中调用原生方法的开销(SocketInputStream.socketRead0() 是原生的)?

你怎么看?还有其他原因吗?

【问题讨论】:

这似乎有点奇怪,但并非不可能。你能提供更多的上下文吗? “本土网络应用程序”是否直接使用套接字?你读的是单字节吗? “观察”下的更多上下文 您是否知道堆栈中进一步的内容或读取的大小?我可以想象如果你达到你的数字,例如尝试从无缓冲的套接字进行 JSON 解析。 @themel 我们正在使用 Spring 的 RestTemplate,所以我想我们应该安全。但我会检查一下。平均响应大小约为几千字节。 socketRead 将是关于请求解析,而不是响应(除非您正在使用其他人的服务)。 【参考方案1】:

我也面临同样的问题。我的应用程序的 qps 非常高,每个请求都会让我发送多个使用这个原生 api 的 thrift 调用:socketRead0

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

这解释了两件事:

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

    您的 java 线程仍在 jvm 中运行,因为线程状态为 RUNNABLE,这将有助于提高用户空间 cpu 利用率

这两个都会让你的 CPU 很忙。

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

【讨论】:

这种现象在***.com/questions/20475290/…中有解释 @DenisBazhenov 想说的是,在这种情况下,100% CPU 是程序本身的总和,而不是整个 CPU 的总和。因此,如果您的程序占用 5% 的 CPU,并且 99% 用于 socketRead,这意味着您的程序将 99% 的时间用于 socketRead 函数的程序仅占用 5% CPU,这可能不是无论如何从插座。所以最好不要调用 Sleep(),否则可能会占用 100%。【参考方案2】:

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

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

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

【讨论】:

以上是关于SocketInputStream.socketRead0() 中 CPU 使用率高的原因的主要内容,如果未能解决你的问题,请参考以下文章