在分析本土networking应用程序的时候,我发现下面非常奇怪(至less对我来说)是观察的。
几乎所有的时间都花在SocketInputStream
类的socketRead0()
方法中。 这并不奇怪,因为我的应用程序在每个请求上都与远程服务联网。 奇怪的是,这种方法不仅壁挂时钟使用率高, CPU时钟时间也很高。 我不明白为什么CPU时间很长,因为如果我的应用程序等待远程服务来回复(实际上这不是很快),那么应用程序本身就没有什么可做的了。 所以CPU时间应该显然很低。
还有一些观察:
SocketInputStream.socketRead0()
正在消耗高达95%的时间(挂钟时间和 CPU时间); mpstat
(我们使用Linux作为操作系统)显示约90%的用户时间和约1-3%的系统时间(其余为空闲时间); RestTemplate
与远程服务交互,而不是直接与SocketInputStream
交互。 现在我只有一个想法 – 也许这是在JVM中调用本地方法的开销( SocketInputStream.socketRead0()
是本地的)?
你怎么看? 有没有其他的原因呢?
VisualVM显示加载不是绝对值,而是相对值,所以它只是意味着你的应用程序没有更多的CPU消耗点。
我相信你应该配置VisualVM不深入深入,而是计数这个方法调用作为你的代码(或春天)的方法的一部分。
我已经经历过这样的行为,但看起来并不需要任何优化。 Web应用程序只需从套接字(即HTTP请求,数据库,内部网络服务…)读取数据,并没有帮助。
我面临同样的问题。 我的应用程序有一个非常高的qps和每个请求会让我发送多个节俭调用,这使用这个本地api: socketRead0
所以我决定做一个实验。 在返回之前,我使用一个api睡眠模拟服务器,客户端调用这个api。 我的目的是测试网络io发生时的线程状态。 根据我的线程转储,线程状态是RUNNABLE
。
这解释了两件事情:
高qps阻塞io的应用将面临高cpu负载值
你的java线程仍然在jvm中运行,因为线程状态是RUNNABLE
,这将有助于提高用户空间的cpu利用率
这些都会让你的CPU很忙。
在实验中我注意到,系统空间的cpu利用率很低。 我认为这与jvm和os之间的线程调度策略差异有关。 我们知道热点线程模型是1:1,意思是一个线程到一个线程。 当发生阻塞IO时,如socketRead0
,内核线程将设置为状态S
,不会阻塞CPU,但用户空间线程正在阻塞(等待)。 发生这种情况时,我认为我们需要重新思考应用程序中的基本I / O模型。