虽然剖析自主开发的网络应用程序,我碰到下面很奇怪(至少对我来说)观察。
几乎所有的时间都花在socketRead0()
一个方法SocketInputStream
类。 因为我的应用程序做一个网络上每个请求远程服务,这并不奇怪。 什么奇怪的是,不仅挂钟时间使用率很高这种方法,CPU时钟时间也非常高。 我不明白为什么CPU时间很长,因为如果我的申请等待远程服务回复(这实际上不是那么快),没有什么做的留给应用程序本身。 所以CPU时间应该是明显偏低。
一些更多的观察:
- VisualVM的在采样模式示出了方法
SocketInputStream.socketRead0()
是吃高达95%的时间(两个壁时钟时间和 CPU时间); -
mpstat
(我们使用Linux作为操作系统)示出左右〜90%的用户时间和〜1-3%的系统时间(其余为空闲时间); - 应用程序部署在专用服务器上;
- 远程服务是HTTP web的应用也。 平均响应时间是100ms左右。 平均响应大小约为2K位。
- 我的应用程序使用Spring
RestTemplate
与远程服务,而不是互动SocketInputStream
直接。
现在我只有一个想法-也许这就是调用JVM的本地方法的开销( SocketInputStream.socketRead0()
是天然的)?
你怎么看? 是否有其他原因呢?
VisualVM的显示负载不是绝对值,而是相对值,所以它只是意味着你的应用程序没有任何更多的CPU消耗点。
我相信你应该配置的VisualVM不钻在内心深处,和而算上调用此方法,因为这是在你的代码(或弹簧的)的方法的一部分。
我已经经历过这样的行为,但它并没有看起来就像是不需要任何优化。 Web应用程序只需简单地读取套接字数据(即HTTP请求,数据库,内部网络服务...)并没有帮助它。
我面临着同样的问题。 我的应用程序有一个非常高的QPS并且每个请求都会让我发送多个节俭调用,使用原生API: socketRead0
所以,我决定做一个实验。 我就与一个API睡眠30多岁的模拟服务器返回之前和客户端调用此API。 我的目的是测试线程状态时的净IO发生。 根据我的线程转储,该线程状态为RUNNABLE
。
这说明两点:
应用具有高QPS阻塞IO将面临较高的CPU负荷值
你的java线程仍然在JVM上运行,因为线程状态是RUNNABLE
这将有助于提高用户空间cpu利用率
这两个都会让你的CPU忙。
我注意到,在实验过程中,系统空间的CPU利用率很低。 我觉得这个东西涉及JVM和操作系统之间的线程调度策略的差异。 我们知道热点的线程模型是1:1,这意味着一个JVM线程一个操作系统线程。 当阻塞IO发生,如socketRead0
内核线程将设置为状态S
并且不会阻塞CPU,但用户空间线程被阻塞(等待)。 当发生这种情况,我认为我们需要重新思考的基本I / O模型在我们的应用程序。