【问题标题】:What do I need to know when maintaining a Java app with a large number of threads?维护具有大量线程的 Java 应用程序时我需要了解什么?
【发布时间】:2011-08-08 15:16:55
【问题描述】:

背景信息

我有一个进行数据分析的分布式处理应用程序。它旨在对实时更新的多组数据进行并行处理。作为设计的一部分,分析被分解为分析节点。每个节点获取源数据并对其进行处理以创建其他数据,然后这些数据又可以被其他节点使用。要对一个数据集进行我们目前的全套分析,需要大约 200 个节点。

在当前设计中,每个节点都使用自己的线程运行。现在,大多数时候这些线程都处于休眠状态。每当数据更新时,它们就会像瀑布一样依次唤醒每个人,然后又重新进入睡眠状态。该应用程序目前在生产中运行 40 组数据,每组需要 200 个节点,使用 8000 个线程。当没有数据进来时,服务器上没有负载。当数据在最繁忙的时候进来时,服务器会飙升到大约 25% 的 CPU。这一切都在项目的设计和生产参数范围内。

现在下一步,我们将 40 组数据扩展到 200 个。每组需要 200 个节点,这意味着总共有 40000 个节点,即 40000 个线程。这超过了我们服务器的最大 PID,所以我要求我们的服务器管理员增加上限。他们做到了,应用程序工作正常,但他们给了我一些关于线程数量的回击。我不否认线程数不寻常,但这是我们设计阶段的预期和保证。

我正在计划对设计进行一些小的调整,以将线程与节点分开。这将允许我们配置一个线程来运行多个节点,并减少我们的线程数。对于不经常更新的数据集,让一个线程在每个节点中执行数据更新对性能的影响很小。对于每秒更新数百次的数据集,我们可以将每个节点配置为在自己的线程上运行。事实上,我不怀疑这种设计改变会发生——这只是时间问题。同时,我想尽可能多地了解使用此设计的后果。

问题

在一台机器上运行超过 40,000 个线程的成本是多少?让 JVM / Linux 操作系统管理这么多线程,我损失了多少性能?请记住,它们都已正确配置为在没有工作时休眠。所以,我只是在谈论额外的开销和由大量线程引起的问题。

请注意 - 我知道我可以减少线程数,而且我知道进行这种设计更改是个好主意。我会尽快完成,但必须与其他工作和设计考虑因素相平衡。我问这个问题是为了收集信息以便做出正确的决定。非常感谢您对这种性质的想法和看法。

【问题讨论】:

    标签: java multithreading performance optimization


    【解决方案1】:

    在一台机器上运行超过 40,000 个线程的成本是多少?让 JVM / Linux 操作系统管理这么多线程,我损失了多少性能?请记住,它们都已正确配置为在没有工作时休眠。所以,我只是在谈论额外的开销和由大量线程引起的问题。

    在JVM空间中,每个线程都需要一个线程栈(默认256kb)以及Thread对象和连接对象。可以使用 -Xss 选项更改默认线程堆栈,但我相信 64kb 是下限。 (40,000 x 256kb 是 10Gb ...)

    在 Linux 上,每个线程还占用一个操作系统线程描述符,当线程不执行时,这将有助于线程的寄存器上下文......和其他东西。这些描述符是预先分配的,我相信它们没有分页。这是您的管理员需要增加的资源。

    无论线程是唤醒还是休眠,都会使用这些资源。

    另一个问题是您需要小心使用 wait / notifyAll 进行同步。如果有很多线程在同一个对象上等待,那么 notifyAll 将在每个线程被唤醒时引发一系列活动。 (但您可以通过不在同一个对象上等待大量线程来避免这种情况。)

    有关使用大量线程的后果的更多信息,请参阅 Oracle Java Threading 页面。


    我的感觉是 40,000 个线程过多。理想的线程数与您拥有的物理处理器/内核数成正比。虽然拥有大量线程不一定会降低性能,但会占用大量资源,这可能会产生间接的性能问题;例如更长的 GC 时间,潜在的 VM 抖动。

    更适合您的应用程序的架构是实现线程池和工作队列,以将工作分配给数量少得多的活动线程。

    【讨论】:

    • +1 表示关于线程数量过多和使用执行器服务的好评论。
    • 成功了。谢谢您的帮助。这不应该是一个困难的解耦。现在我明白为什么他们说扩展Thread 不好。没有什么比经验更能教人的了。
    【解决方案2】:

    现在你说没有工作时线程会休眠。多久会有一次工作?有多少工作单元同时进行?如果该数字大于处理器的数量,并且所述工作主要基于 CPU,那么您实际上会看到整体性能下降。

    但是让我们假设在任何给定时间完成的工作量是处理器的数量。如果是这种情况,我可以看到的第一个问题是将发生的上下文切换量。 Java 中的上下文切换(通常基于)大约有 100 个指令。如果你的所有线程在短时间内都被切换到(唤醒)来完成他们的一些工作,那么我们正在谈论超过 4,000,000 条额外的指令。

    更多关于上下文切换成本的信息,因为它们可能对你的程序的影响比任何事情都大。此document 的摘录解释了在切换时验证线程的本地缓存的成本

    当一个新线程被切换时, 它需要的数据不太可能在 本地处理器缓存,所以一个上下文 开关导致一连串的缓存 未命中,因此线程运行了一点 当他们第一次出现时更慢 预定。这是原因之一 调度程序给每个可运行的 线程某个最小时间量 即使有许多其他线程 等待

    除此之外,您还需要分配额外的堆栈空间,还有用于 40,000 个线程对象的堆(线程只有大约 7 兆的浅堆)。

    【讨论】:

    • 好的,如果我将一些较小的数据集折叠成仅使用一个线程,我可以看到一些优化。当它沿着分析树向下移动时,它在第一步中生成的数据将被第二步使用,依此类推,并且这些数据更有可能在处理器缓存中徘徊。拆分后,无法保证每个连续步骤甚至会在同一个处理器中唤醒。这似乎不是一个足够严重的问题,在我们可以安排下一部分工作之前,我们不能在这种设计下运行一段时间。
    猜你喜欢
    • 2010-09-08
    • 2011-06-18
    • 1970-01-01
    • 2012-02-21
    • 1970-01-01
    • 1970-01-01
    • 2016-06-10
    • 2019-09-12
    • 1970-01-01
    相关资源
    最近更新 更多