【问题标题】:Thread.sleep and BufferedReader.readLine use the most cpu cycles in my java tcp server. Why?Thread.sleep 和 BufferedReader.readLine 在我的 java tcp 服务器中使用最多的 cpu 周期。为什么?
【发布时间】:2011-06-29 20:00:43
【问题描述】:

晚上好,

我正在开发一个用于客户端之间通信的 java tcp 服务器。

此时我正在对开发的服务器进行负载测试。

今天早上我得到了一个分析器(yourkit)并开始在我的服务器中寻找问题点。

我现在有 480 个客户端每 500 毫秒向服务器发送消息。服务器将收到的每条消息转发给 6 个客户端。

当处于恒定负载时,服务器现在使用了我大约 8% 的 cpu。

我的问题是关于使用最多 cpu 周期的 java 函数。

使用cpu周期最多的java函数奇怪的是“Thread.sleep”,其次是“BufferedReader.readLine”。

这两个函数似乎在等待某些东西时阻塞了当前线程(sleep 等待几毫秒,readline 等待数据)。

有人能解释一下为什么这两个函数会占用那么多 CPU 周期吗?我还想知道是否有使用更少 CPU 周期的替代方法。

亲切的问候, T. Akhayo

【问题讨论】:

    标签: java profiling


    【解决方案1】:

    sleep() 和 readLine() 可以使用大量 cpu,因为它们都会导致可以上下文切换的系统调用。由于这个原因,这些方法的时间也可能不准确(可能被高估了)

    减少上下文切换/sleep() 开销的一种方法是减少线程并避免需要使用睡眠(例如使用 ScheduledExecutorServices),使用 NIO 可以减少 readLine() 开销,但可能会增加一些复杂性。

    【讨论】:

    • 感谢您提供 ScheduledExecutorServices 提示。我还没有机会尝试它,但它看起来不错。虽然当我在一秒钟内安排许多任务时我有点担心开销,但测试是知道的:)
    • 使用过多线程的开销可能会花费更多。开销是 1 到 10 我们。如果你每 1 毫秒调用一次 sleep 就会有问题。
    【解决方案2】:

    睡眠不应该是一个问题,除非你有一堆线程在短时间内睡眠(当你有 480 个线程运行一个循环时,100-150 毫秒是“短”的)只是睡眠并做一些微不足道的事情)。

    当 readLine 调用实际上没有读取某些内容时,它应该几乎不使用任何内容,除非您第一次调用它。但就像你说的那样,它会阻塞,除非你有阻塞的小窗口,否则它不应该使用大量的 CPU。除非您正在读取大量数据或最初调用该方法,否则 CPU 使用率不会那么高。

    因此,您的循环太紧了,而且您太快地接收到太多消息,这最终会导致“大量”上下文切换和处理。如果您对 NIO 不够满意,无法自己使用它,我建议您使用 NIO 框架(如 Netty)。

    此外,对于每秒发送 2 条消息的 480 个客户端而言,8% 的 CPU 并不多。

    【讨论】:

    • 我确实有大约 480 个线程睡眠 100 毫秒,它们确保客户端每 100 毫秒只接收 1 个数据包。我将看看我是否可以将这些线程的工作组合成大约 80 个线程,这有望解决睡眠问题。 readline 确实每秒读取 2 条消息,我认为这不是问题。我之前用过netty,但现阶段似乎有点矫枉过正,让我们等到需要它。正如你所说,8% 并不算多,但我怀疑当引入更多客户时它会增长得非常快。
    【解决方案3】:

    这是一个程序,其中sleep 使用了几乎 100% 的分配给应用程序的 cpu 周期:

    for (i = 0; i < bigNumber; i++){
      sleep(someTime);
    }
    

    为什么?因为它根本不使用很多实际的 cpu 周期, 在它确实使用的那些中,几乎所有这些都花在进入和离开sleep

    这是否意味着这是一个真正的问题?当然不是。

    That's the problem with profilers that only look at CPU time.

    您需要一个按挂钟时间而非 CPU 时间进行采样的采样器。 它应该对堆栈进行采样,而不仅仅是程序计数器。 它应该按代码行(而不是按函数)向您显示包含该行的堆栈样本的分数。

    通常反对按挂钟时间采样的原因是,由于与其他进程共享机器,测量结果会不准确。 但这没关系,因为要找到时间消耗并不需要精确的测量。 它需要位置的精度。 您正在寻找的是精确的代码位置和调用站点,它们在堆栈上是实际时间的健康分数,由与程序状态不相关的堆栈采样确定。 与其他进程的竞争不会将调用站点在堆栈上的时间比例改变到足以导致遗漏问题的程度。

    【讨论】:

      猜你喜欢
      • 2017-11-28
      • 2012-07-08
      • 1970-01-01
      • 2014-05-05
      • 2011-02-09
      • 2011-10-10
      • 2015-08-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多