【问题标题】:Problems caused due to explicitly creating threads in java由于在 java 中显式创建线程而导致的问题
【发布时间】:2014-07-07 13:50:04
【问题描述】:

我正在处理WAS 6.1 中的以下OutOfMemory 异常。

Exception in thread "UnitHoldingsPolicySummary" java.lang.OutOfMemoryError: unable to create new native thread.

我已经对此进行了大量研究以防止这种情况发生。谷歌搜索后,我发现,当 Native 内存由于同时创建大量线程而耗尽时,就会发生这种情况。

现在,在分析下面的日志之后,我们可以发现,在应用程序内部,线程是显式创建的,我读到这是一个非常非常糟糕的做法。 (请专家确认一下?)

07/07/14 08:50:38:165 BST] 0000142c SystemErr     R Exception in thread "xxxxxx" java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Thread.java:574)
        at com.fp.sv.controller.business.thread.xxxxxxxxxexecute(Unknown Source)
        at com.fp.sv.controller.business.thread.xxxxxxxxx.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:595) 

我更喜欢 WAS 管理,但对 Java 和 Java 中的线程创建知之甚少。现在我需要与开发人员讨论这个问题,但在此之前我想 100% 确认我的发现是正确的,开发人员应该通过不明确创建线程来更正代码。

在将此归咎于代码之前,我需要在应用服务器端检查哪些内容?

在 solaris 上,我正在触发命令 pmap -x 9547|grep -i stack|wc -l 以检查在该时间实例上创建了多少线程。我可以在“OutOfMemory”问题期间看到,这个数字非常高。

您能否确认此命令是否是检查当前活动线程数的好方法?

用我的最新发现编辑问题

此外,当此问题发生时,同时一个 MQ 队列会堆积起来,因为 WAS 不会从队列中提取消息。我可以在特定于应用程序的日志中看到以下错误。

Non recoverable Exception detected whilst connecting to queue manager or response queue Underlying reason = MQJE001: Completion Code 2, Reason 2102

这个问题是否也与 MQ 相关?这又会导致 OutOfMemory 问题?

问候, 拉胡尔

【问题讨论】:

    标签: java multithreading websphere out-of-memory ibm-mq


    【解决方案1】:

    为虚拟机实现线程系统有多种可能性。两种极端形式是:

    1. 绿色线程:所有 Java Thread 实例都在一个本机操作系统线程中进行管理。如果一个方法在 native 调用中阻塞,这可能会导致问题,这使得该实现变得复杂。最后,实现者需要引入叛徒线程来持有本地锁来克服这些限制。
    2. 本机线程:每个 Java Thread 实例都由本机操作系统线程支持。

    对于绿色线程的命名限制,所有现代 JVM 实现,包括 HotSpot,都选择较晚的实现。这意味着操作系统需要为每个创建的线程保留一些内存。此外,创建这样的线程需要一些运行时开销,因为它需要与底层操作系统直接交互。在某些时候,这些成本会累积起来,操作系统会拒绝创建新线程,从而影响整个系统的稳定性。

    因此应将线程池化以供重新使用。对象池通常被认为是不好的做法,因为许多程序员使用它来简化 JVM 的垃圾收集器。这不再有用,因为现代垃圾收集器已针对处理短期对象进行了优化。今天,通过汇集对象,您可能反而会减慢您的系统速度。但是,如果对象由昂贵的本机资源支持(如Thread),则池化仍然是推荐的做法。查看ExecutorService 以了解在 Java 中池化线程的规范方法。

    一般来说,考虑线程的上下文切换是昂贵的。你不应该为小任务创建一个新线程,这会减慢你的应用程序。而是使您的应用程序的并发性降低。首先,您只有多个可以同时工作的内核,创建比您的(非虚拟)内核更多的线程不会提高运行时性能。您是否正在实施某种分而治之的算法?查看 Java 的 ForkJoinPool

    【讨论】:

      【解决方案2】:

      是的,这是一种不好的做法。通常,您不管理 Java EE 服务器内的线程。 “通常”是指“在开发业务应用程序时”。

      根据http://www.oracle.com/technetwork/java/restrictions-142267.html

      为什么不允许创建和管理线程?

      EJB 规范将责任分配给 EJB 容器 用于管理线程。允许企业 bean 实例创建和 管理线程会干扰容器的控制能力 其组件的生命周期。线程管理不是一门生意 函数,它是一个实现细节,通常很复杂 和平台特定的。让容器管理线程可以缓解 处理线程问题的企业 bean 开发人员。 多线程应用程序仍然是可能的,但控制 多线程位于容器中,不在企业中 豆子。

      但是,我不认为您的日志表明正在显式创建线程。如果您想 100% 确定,请反编译可部署文件并查看这些行中的代码。

      也看看这个:

      "java.lang.OutOfMemoryError : unable to create new native Thread"

      还有这个:

      https://plumbr.eu/outofmemoryerror/unable-to-create-new-native-thread

      关于您的应用使用的线程数,我会尝试使用 JConsole 或 VisualVm 等监控工具。

      【讨论】:

      • 谢谢!我发布的日志是否确认线程是被明确创建的?
      • 通常,您不会在 JEE 服务器内管理线程。 当然可以。不是所有线程,但您可以集成自己的代码,当然可以管理自己的线程来执行任务。
      • @Andres 不错的报复性否决票,有课。我相信您的回答是不正确的,我向您解释了原因:您不管理 JEE 服务器内的线程 的说法完全是错误的。有了多个处理器可用,编写多线程应用程序并将它们部署到 JEE 容器中不是问题。这不是问题,OP 有。问题是线程是显式创建的,而不是使用池。解决方案是管理线程,而不是摆脱它们。
      • 它表示 EJB 容器对其管理的组件设置了某些限制,然后列出了这些限制。是什么让您认为创建的线程与这样的组件相关?顺便说一句,这两者都不能证明报复性投票是合理的。
      • Java EE 有一些明确的机制可用于创建 托管 线程。托管线程是那些保留一些上下文的线程,例如 Java EE 组件名称空间、经过身份验证的身份(如果有)、事务状态等。我在这里收集了一些关于该主题的链接:javaee7.zeef.com/arjan.tijms#block_237 但如果你不需要这个上下文(例如,做一些原始计算,或者通过从 java:global 空间中查找 EJB bean 来引导它们)并且您在池中管理您的线程,而不是恕我直言,处理您自己的线程没有任何问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-16
      • 1970-01-01
      • 1970-01-01
      • 2018-03-14
      • 2021-08-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多