dag调度程序事件循环java.lang.outofmemoryerror:无法创建新的本机线程

zte4gxcn  于 2021-05-27  发布在  Spark
关注(0)|答案(3)|浏览(458)

在运行了5-6个小时后,我从spark驱动程序中得到以下错误。我使用的是ubuntu16.04 lts和open-jdk-8。

Exception in thread "ForkJoinPool-50-worker-11" Exception in thread "dag-scheduler-event-loop" Exception in thread "ForkJoinPool-50-worker-13" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:714)
    at scala.concurrent.forkjoin.ForkJoinPool.tryAddWorker(ForkJoinPool.java:1672)
    at scala.concurrent.forkjoin.ForkJoinPool.deregisterWorker(ForkJoinPool.java:1795)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:117)
java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:714)
    at scala.concurrent.forkjoin.ForkJoinPool.tryAddWorker(ForkJoinPool.java:1672)
    at scala.concurrent.forkjoin.ForkJoinPool.signalWork(ForkJoinPool.java:1966)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.push(ForkJoinPool.java:1072)
    at scala.concurrent.forkjoin.ForkJoinTask.fork(ForkJoinTask.java:654)
    at scala.collection.parallel.ForkJoinTasks$WrappedTask$class.start(Tasks.scala:377)
    at scala.collection.parallel.AdaptiveWorkStealingForkJoinTasks$WrappedTask.start(Tasks.scala:443)
    at scala.collection.parallel.AdaptiveWorkStealingTasks$WrappedTask$$anonfun$spawnSubtasks$1.apply(Tasks.scala:189)
    at scala.collection.parallel.AdaptiveWorkStealingTasks$WrappedTask$$anonfun$spawnSubtasks$1.apply(Tasks.scala:186)
    at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
    at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
    at scala.collection.parallel.AdaptiveWorkStealingTasks$WrappedTask$class.spawnSubtasks(Tasks.scala:186)
    at scala.collection.parallel.AdaptiveWorkStealingForkJoinTasks$WrappedTask.spawnSubtasks(Tasks.scala:443)
    at scala.collection.parallel.AdaptiveWorkStealingTasks$WrappedTask$class.internal(Tasks.scala:157)
    at scala.collection.parallel.AdaptiveWorkStealingForkJoinTasks$WrappedTask.internal(Tasks.scala:443)
    at scala.collection.parallel.AdaptiveWorkStealingTasks$WrappedTask$class.compute(Tasks.scala:149)
    at scala.collection.parallel.AdaptiveWorkStealingForkJoinTasks$WrappedTask.compute(Tasks.scala:443)
    at scala.concurrent.forkjoin.RecursiveAction.exec(RecursiveAction.java:160)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinTask.doJoin(ForkJoinTask.java:341)
    at scala.concurrent.forkjoin.ForkJoinTask.join(ForkJoinTask.java:673)
    at scala.collection.parallel.ForkJoinTasks$WrappedTask$class.sync(Tasks.scala:378)
    at scala.collection.parallel.AdaptiveWorkStealingForkJoinTasks$WrappedTask.sync(Tasks.scala:443)
    at scala.collection.parallel.ForkJoinTasks$class.executeAndWaitResult(Tasks.scala:426)
    at scala.collection.parallel.ForkJoinTaskSupport.executeAndWaitResult(TaskSupport.scala:56)
    at scala.collection.parallel.ParIterableLike$ResultMapping.leaf(ParIterableLike.scala:958)
    at scala.collection.parallel.Task$$anonfun$tryLeaf$1.apply$mcV$sp(Tasks.scala:49)
    at scala.collection.parallel.Task$$anonfun$tryLeaf$1.apply(Tasks.scala:48)
    at scala.collection.parallel.Task$$anonfun$tryLeaf$1.apply(Tasks.scala:48)
    at scala.collection.parallel.Task$class.tryLeaf(Tasks.scala:51)
    at scala.collection.parallel.ParIterableLike$ResultMapping.tryLeaf(ParIterableLike.scala:953)
    at scala.collection.parallel.AdaptiveWorkStealingTasks$WrappedTask$class.compute(Tasks.scala:152)
    at scala.collection.parallel.AdaptiveWorkStealingForkJoinTasks$WrappedTask.compute(Tasks.scala:443)
    at scala.concurrent.forkjoin.RecursiveAction.exec(RecursiveAction.java:160)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:714)
    at scala.concurrent.forkjoin.ForkJoinPool.tryAddWorker(ForkJoinPool.java:1672)
    at scala.concurrent.forkjoin.ForkJoinPool.deregisterWorker(ForkJoinPool.java:1795)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:117)

这是默认情况下在客户机模式下运行的spark驱动程序产生的错误,因此有人说只需通过传递 --driver-memory 3g 不管消息是怎么写的 "unable to create new native thread" 实际上,jvm请求os创建一个新线程,但os无法再分配它,jvm通过请求os可以创建的线程数依赖于平台,但在64位os&jvm上通常是32k个线程。
当我做了ulimit-a时,我得到了以下信息

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 120242
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 120242
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

cat/proc/sys/kernel/pid\最大值

32768

cat/proc/sys/kernel/threads最大值

240484

“无法创建新的本机线程”显然意味着它与堆无关。所以我认为这更像是操作系统的问题。

qyswt5oh

qyswt5oh1#

spark2.0.0中forkjoinpool的使用似乎有一个bug,它创建了太多的线程。特别是在unionrdd.scala中,当您在dstream上调用窗口操作时使用它。
https://issues.apache.org/jira/browse/spark-17396 因此,根据这张票,我升级到2.0.1,它修复了这个问题。

iq0todco

iq0todco2#

在java中,您可能会遇到两种内存不足错误:
这个 java.lang.OutOfMemoryError Java heap space error :当应用程序尝试将更多数据分配到堆空间区域,但空间不足时,将触发此异常。尽管您的机器上可能有大量可用内存,但您已经达到了jvm允许的最大内存量,这可以通过-xmx参数设置
这个 java.lang.OutOfMemoryError: Unable to create new native thread 每当jvm从操作系统请求新线程时发生。如果底层操作系统无法分配新的本机线程,则会抛出此outofmemoryerror。
1) 检查线程系统范围的设置
/proc/sys/kernel/threads max文件提供了系统范围内的线程数限制。根用户可以更改该值,如果他们希望:

$ echo 100000 > /proc/sys/kernel/threads-max

您可以通过/proc/loadavg文件系统检查当前正在运行的线程数:

$ cat /proc/loadavg
0.41 0.45 0.57 3/749 28174

看第四场!此字段由斜杠(/)分隔的两个数字组成。第一个是当前正在执行的内核调度实体(进程、线程)的数量;这将小于或等于CPU的数量。斜杠后面的值是系统上当前存在的内核调度实体的数量。在本例中,您正在运行749个线程
2) 检查每个用户的进程数
在linux机器上,线程本质上只是具有共享地址空间的进程。因此,您必须检查您的操作系统是否允许为用户提供足够的进程。可通过以下方式进行检查:

ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 515005
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 4096
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

默认情况下,每个用户的进程数默认为1024。此时,我们将计算正在运行的进程数。运行的进程数可以用ps输出计数:

$ ps -elf | wc -l  
220

但是,这个数字不考虑进程可以派生的线程。如果您尝试使用a-t运行ps,您也会看到所有线程:

$ ps -elfT | wc -l  
385

如您所见,由于线程的存在,进程计数显著增加。通常这不是任何类型的问题,但是在基于java的应用程序中,这会导致系统运行到系统极限!我们继续调查吧。让我们看看您的jboss进程生成了多少线程。你至少可以用两种方法来做:

$ ps -p JBOSSPID -lfT | wc -l

上面的shell将返回为pid指示的进程创建的轻量级进程数。这应该与jstack生成的线程转储计数相匹配:

$ jstack -l JBOSSPID | grep tid | wc -l

现在您应该有证据表明您需要为用户增加进程的数量。这可以通过以下命令完成:

$ ulimit -u 4096

3) 检查你的线程pid限制
一旦计算了线程数,就应该验证没有达到kernel.pid\u max limit参数指定的系统限制。您可以通过执行以下操作来检查此值:

$ sysctl -a | grep kernel.pid_max  

kernel.pid_max = 32768

4) 减小线程堆栈大小
如果无法修改操作系统设置,可以使用的另一个选项是减小堆栈大小。jvm有一个有趣的实现,通过这个实现,为堆分配的内存越多(堆不一定使用),堆栈中可用的内存就越少,而且由于线程是从堆栈生成的,实际上,这意味着堆意义上的更多“内存”(这通常是人们所谈论的)会导致能够并发运行的线程更少。
首先检查默认线程堆栈大小,该大小取决于您的操作系统:

$  java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
     intx ThreadStackSize                           = 1024                                {pd product}

如您所见,在我们的机器中,默认的线程堆栈大小是1024KB。为了减少堆栈大小,在jvm选项中添加“-xss”选项。在jbosseap6/wildfly中,最小线程堆栈大小是228kb。您可以在独立模式下通过改变java\u选项来更改它,如下例所示:

JAVA_OPTS="-Xms128m -Xmx1303m -Xss256k"

在域模式下,可以在不同级别(主机、服务器组、服务器)配置jvm元素。在这里,您可以设置请求的堆栈大小,如下一节所示:

<jvm name="default">
            <heap size="64m" max-size="256m"/>
            <jvm-options>
                <option value="-server"/>
                <option value="-Xss256k"/>
            </jvm-options>
</jvm>

资源链接:

如何解决java.lang.outofmemoryerror:无法创建新的本机线程

5lhxktic

5lhxktic3#

根本原因分析:

jvm中的线程需要一些空间来执行它们被召唤来处理的工作。当内存中的线程多于内存空间时,我们为问题建立了基础:

信息 java.lang.OutOfMemoryError: Unable to create new native thread 意味着java应用程序已经达到了它可以启动的线程数的极限。

是什么引起的?

你有机会面对 java.lang.OutOfMemoryError: Unable to create new native thread 每当jvm从操作系统请求一个新线程时。每当底层操作系统无法分配新的本机线程时,这 OutOfMemoryErro r将被抛出。本机线程的确切限制非常依赖于平台,因此我们建议通过运行类似于下面示例的测试来找出这些限制。但是,通常情况下,导致java.lang.outofmemoryerror:无法创建新的本机线程的情况会经历以下阶段:
在jvm中运行的应用程序请求一个新的java线程
jvm本机代码代理向os创建新本机线程的请求
操作系统试图创建一个新的本机线程,该线程需要为该线程分配内存
操作系统将拒绝本机内存分配,因为32位java进程大小耗尽了其内存地址空间(例如,达到了(2-4)gb进程大小限制),或者操作系统的虚拟内存已完全耗尽
抛出java.lang.outofmemoryerror:无法创建新的本机线程错误。

给我举个例子

下面的示例在循环中创建并启动新线程。在运行代码时,操作系统的限制会快速达到 java.lang.OutOfMemoryError: Unable to create new native thread 显示消息。

while(true){
    new Thread(new Runnable(){
        public void run() {
            try {
                Thread.sleep(10000000);
            } catch(InterruptedException e) { }        
        }    
    }).start();
}

确切的本机线程限制取决于平台,例如,在windows、linux和mac os x上的测试表明:
64位mac os x 10.9、java 1.7.0Š45–在创建了Š2031个线程之后,jvm死机
64位ubuntu linux,java 1.7.0Š45–jvm在创建了Š31893个线程后死亡
64位windows7,java1.7.0\u45–由于操作系统使用不同的线程模型,这个错误似乎不会在这个特定的平台上抛出。在线程#250000上,进程仍然处于活动状态,尽管交换文件已增长到10gb,并且应用程序面临极端的性能问题。因此,请通过调用一个小测试来确定您的限制,并确定何时会触发java.lang.outofmemoryerror:unable to create new native thread

解决办法是什么?

有时,您可以通过增加操作系统级别的限制来绕过“无法创建新的本机线程”问题。例如,如果您限制了jvm可以在用户空间中生成的进程数,则应该 checkout 并可能增加限制:

[root@dev ~]# ulimit -a
core file size          (blocks, -c) 0
--- cut for brevity ---
max user processes              (-u) 1800

通常,outofmemoryerror对新的本机线程的限制表明存在编程错误。当您的应用程序产生数千个线程时,很有可能出现了严重的问题—没有多少应用程序可以从如此大量的线程中获益。解决这个问题的一种方法是开始使用线程转储来了解情况。

资源链接:

https://plumbr.eu/outofmemoryerror/unable-to-create-new-native-thread
还有更多其他答案,请点击此处:
如何解决java.lang.outofmemoryerror:无法创建新的本机线程
outofmemoryerror:无法创建新的本机线程-问题已揭秘
https://stackoverflow.com/a/16789621/2293534
java:无法创建新的本机线程

相关问题