高CPU,可能是由于上下文切换?

时间:2012-03-02 14:43:00

标签: java multithreading

我们的一台服务器在使用我们的应用程序时遇到了非常高的CPU负载。我们已经查看了各种统计数据,并且在找到问题根源时遇到了问题。

目前的理论之一是涉及太多线程,我们应该尝试减少并发执行线程的数量。只有一个主线程池,有3000个线程,还有一个使用它的WorkManager(这是Java EE - Glassfish)。在任何给定时刻,需要并行执行大约620个单独的网络IO操作(也不能使用java.NIO)。此外,大约有100个操作没有涉及IO,也可以并行执行。

这种结构效率不高,我们希望看看它是否真的造成了损害,或者只是不好的做法。原因是在这个系统中任何变化都非常昂贵(就工时而言)所以我们需要一些问题的证据。

所以现在我们想知道线程的上下文切换是否是原因,因为线程比所需的并发操作多得多。查看日志,我们发现在给定的秒内平均执行了14个不同的线程。如果我们考虑到两个CPU的存在(见下文),则每个CPU有7个线程。这听起来不太多,但我们想验证这一点。

那么 - 我们可以排除上下文切换或太多线程作为问题吗?

一般细节:

  1. Java 1.5(是的,它已经老了),运行在CentOS 5,64位,Linux内核2.6.18-128.el5
  2. 机器上只有一个Java进程,没有别的。
  3. VMware下的两个CPU。
  4. 8GB RAM
  5. 我们无法在计算机上运行探查器。
  6. 我们没有选择升级Java和OS。
  7. 更新 如下所述,我们在各种负载的测试服务器上进行了平均负载(使用正常运行时间)和CPU(使用vmstat 1 120)的捕获。我们在每次负载变化和测量之间等了15分钟,以确保系统在新负载周围稳定并且负载平均数更新:

    生产服务器工作量的50%:http://pastebin.com/GE2kGLkk

    生产服务器工作量的34%:http://pastebin.com/V2PWq8CG

    生产服务器工作量的25%:http://pastebin.com/0pxxK0Fu

    随着负载的减少,CPU使用率似乎会降低,但不是在非常激烈的水平上(从50%变为25%并不是CPU使用率减少50%)。负载平均值似乎与工作负载量无关。

    还有一个问题:鉴于我们的测试服务器也是虚拟机,其CPU测量是否会受到在同一主机上运行的其他虚拟机的影响(使上述测量无效)?

    更新2 以三个部分附加线程的快照(pastebin限制)

    第1部分:http://pastebin.com/DvNzkB5z

    第2部分:http://pastebin.com/72sC00rc

    第3部分:http://pastebin.com/YTG9hgF5

6 个答案:

答案 0 :(得分:4)

我认为你的约束是不合理的。基本上你所说的是:

1.I can't change anything
2.I can't measure anything

你能猜一下我的问题是什么吗?

对此的真正答案是,您需要将正确的探查器连接到应用程序,并且需要将所看到的内容与CPU使用率,磁盘/网络I / O和内存相关联。

记住性能调优的80/20规则。 80%将来自调整您的应用程序。您可能只是为一个VM实例加载了太多负载,并且可能需要考虑通过为机器提供更多资源来水平或垂直扩展的解决方案。可能是30亿JVM设置中的任何一个都不符合您的应用程序的执行细节。

我假设3000线程池来自着名的更多线程=更多并发=更多性能理论。真正的答案是调整更改不值得,除非您在更改之前/之后测量吞吐量和响应时间并比较结果。

答案 1 :(得分:2)

  

那么 - 我们可以排除上下文切换或太多线程作为问题吗?

我认为你担心捶打是有道理的。在2 CPU VMware实例当前上具有3000个线程(700多个并发操作)的线程池似乎是一个可能导致上下文切换过载和性能问题的问题。限制线程数可以为您提供性能提升虽然确定正确的数字将会很困难并且可能会使用大量的反复试验。

  

我们需要一些问题的证明。

我不确定回答的最佳方式,但这里有一些想法:

  • 观察VM OS和JVM的平均负载。如果您看到高负载值(20+),那么这表明运行队列中有太多东西。
  • 是否无法模拟测试环境中的负载,因此您可以使用线程池编号?如果在池大小为X的测试环境中运行模拟加载,然后使用X / 2运行,则应该能够确定最佳值。
  • 您能比较一天的高负荷时间和一天的低负荷时间吗?您能否在这些时间内绘制对延迟的响应数量,以确定您是否可以看到颠簸的转折点?
  • 如果您可以模拟负载,那么请确保您不仅仅根据“消防水管饮料”方法进行测试。您需要可以上下拨动的模拟负载。从10%开始并减慢增加模拟负载,同时观察吞吐量和延迟。您应该能够通过观察吞吐量变平或偏转来查看临界点。

答案 2 :(得分:2)

如果您无法进行配置,我建议您进行一两次线程转储并查看您的线程正在做什么。您的应用无需停止执行此操作:

  1. http://docs.oracle.com/javase/6/docs/technotes/guides/visualvm/threads.html
  2. http://java.net/projects/tda/
  3. http://java.sys-con.com/node/1611555

答案 3 :(得分:2)

对我来说,问题是100个CPU绑定线程比什么都重要。 3000线程池基本上是一个红色的鲱鱼,因为空闲线程不会消耗太多任何东西。 I / O线程可能在“大部分”时间内处于休眠状态,因为I / O是根据计算机操作在地质时间尺度上测量的。

你没有提到100个CPU线程正在做什么,或者它们持续多长时间,但是如果你想减慢计算机速度,那么专门用100个“运行直到时间片说停止”的线程肯定会这样做。因为您有100个“随时可以运行”,所以机器将按照调度程序允许的速度进行上下文切换。空闲时间几乎为零。上下文切换会产生影响,因为您经常这样做。由于CPU线程(可能)消耗了大部分CPU时间,因此您的I / O“绑定”线程将在运行队列中等待的时间比等待I / O的时间长。因此,更多的进程正在等待(I / O进程更频繁地纾困,因为它们很快就会遇到I / O障碍,导致下一个进程无效)。

毫无疑问,这里和那里都有调整以提高效率,但100个CPU线程是100个CPU线程。你可以做的不多。

答案 4 :(得分:1)

通常,线程中的上下文切换在计算上非常便宜,但是当它涉及这么多线程时......你就是无法知道。你说升级到Java 1.6 EE是不可能的,但是有些硬件升级呢?它可能会提供快速解决方案,而且不应该那么昂贵......

答案 5 :(得分:0)

e.g。在类似的机器上运行一个分析器。

  • 尝试更新版本的Java 6或7.(可能没什么区别,在这种情况下不要打扰升级生产)
  • 试试Centos 6.x
  • 尽量不使用VMware。
  • 尝试减少线程数。你只有8个核心。

你们很多人发现上述选项中的所有选项都没有任何区别,但是在你拥有一个系统之前你不会知道,你可以使用已知/可重复的工作量进行测试。