“ java.lang.OutOfMemoryError: ไม่สามารถสร้างเธรดเนทีฟใหม่ได้”


124

เรากำลังได้รับ"java.lang.OutOfMemoryError : unable to create new native Thread"บน 8GB RAM VM หลังจาก 32k เธรด (ps -eLF | grep -c java)

อย่างไรก็ตาม"top" and "free -m" shows 50% free memory available. JDk เป็น 64 บิตและลองกับทั้ง HotSpot และ JRockit เซิร์ฟเวอร์มี Linux 2.6.18

นอกจากนี้เรายังได้ลองOS stack size (ulimit -s)ปรับแต่งและขีด จำกัด กระบวนการสูงสุด (ulimit -u) เพิ่มขึ้น limit.conf แต่ทั้งหมดก็ไร้ผล

นอกจากนี้เราได้ลองชุดค่าผสมขนาดฮีปเกือบทั้งหมดทำให้ต่ำสูงเป็นต้น

สคริปต์ที่เราใช้เพื่อเรียกใช้แอปพลิเคชันคือ

/opt/jrockit-jdk1.6/bin/java -Xms512m -Xmx512m -Xss128k -jar JavaNatSimulator.jar /opt/tools/jnatclients/natSimulator.properties

ขอบคุณสำหรับการตอบกลับ.

เราได้ลองแก้ไข /etc/security/limits.conf และ ulimit แล้ว แต่ก็ยังเหมือนเดิม

[root@jboss02 ~]# 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) 72192
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
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) 72192
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

11
ระบบปฏิบัติการมีข้อ จำกัด เกี่ยวกับจำนวนเธรดที่คุณสามารถสร้างได้ ทำไมคุณถึงสร้างเธรดมากกว่า 32k ระบบของคุณส่วนใหญ่ไม่มีแกนประมวลผลหลายพันคอร์การสร้างเธรดจำนวนมากจึงไม่มีประโยชน์ ใช้เธรดพูล ( ExecutorService) แทน
Jesper

ขอบคุณสำหรับการตอบกลับ. เรากำลังใช้ไลบรารีโอเพ่นซอร์สและพยายามโหลดการทดสอบนั้น ไลบรารีโอเพนซอร์สใด ๆ ที่สร้างเธรดจำนวนมาก แต่สิ่งที่ฉันไม่เข้าใจคือเมื่อ "ด้านบน" แสดงหน่วยความจำว่าง 50% แล้วทำไม OutOfMemory Error
Deepak Tewani

ไลบรารีโอเพนซอร์สที่เราใช้ใน ICE4j Library
Deepak Tewani

11
OutOfMemoryError ไม่ได้แปลว่าพื้นที่ฮีปหรือ RAM "ทั่วไป" หมดลง ในกรณีนี้ชัดเจนว่าความล้มเหลวเกิดจากระบบปฏิบัติการไม่มีทรัพยากรในการจัดสรรเธรดเพิ่มเติม การมีหน่วยความจำว่าง 50% ไม่เกี่ยวข้องกับความล้มเหลวนี้โดยเฉพาะ
Andrzej Doyle

1
ทรัพยากรอื่น ๆ ที่จำเป็นสำหรับการสร้างเธรดใหม่คืออะไร เรารู้สึกว่าถ้าเราเพิ่มแรมเราอาจสร้างเธรดได้มากขึ้น กรุณาแนะนำเรา
Deepak Tewani

คำตอบ:


80

นี่ไม่ใช่ปัญหาหน่วยความจำแม้ว่าชื่อข้อยกเว้นจะแนะนำเป็นอย่างดี แต่เป็นปัญหาทรัพยากรระบบปฏิบัติการ คุณกำลังใช้เธรดเนทีฟหมดนั่นคือจำนวนเธรดที่ระบบปฏิบัติการอนุญาตให้ใช้ JVM ของคุณ

นี่เป็นปัญหาที่ไม่ธรรมดาเพราะคุณแทบไม่ต้องการอะไรมากมายขนาดนั้น คุณมีเธรดที่ไม่มีเงื่อนไขจำนวนมากที่เกิดขึ้นโดยที่เธรดควรจะไม่เสร็จสิ้นหรือไม่?

คุณอาจพิจารณาเขียนใหม่โดยใช้ Callable / Runnables ภายใต้การควบคุมของ Executor ถ้าเป็นไปได้ทั้งหมด มีตัวดำเนินการมาตรฐานมากมายที่มีลักษณะการทำงานที่หลากหลายซึ่งโค้ดของคุณสามารถควบคุมได้อย่างง่ายดาย

(มีสาเหตุหลายประการที่ทำให้จำนวนเธรดถูก จำกัด แต่แตกต่างกันไปในแต่ละระบบปฏิบัติการ)


ขอบคุณสำหรับการตอบกลับ. เราใช้ไลบรารีโอเพนซอร์ส ICE4j และพยายามโหลดการทดสอบนั้น เราไม่สามารถเพิ่มขีด จำกัด ของเธรดใน OS ได้เมื่อเรารู้ว่ามีหน่วยความจำเหลืออยู่ 50% บนเซิร์ฟเวอร์
Deepak Tewani

เป็นไปได้ แต่ฉันคิดว่ามันจะไม่ช่วยให้คุณเป็นเช่นนั้น หากคุณใช้ทรัพยากรไม่เพียงพอเมื่อทำการทดสอบโหลดคุณจะต้องสามารถควบคุมสิ่งที่เกิดขึ้นในแอปพลิเคชันของคุณได้ เหตุใดคุณจึงมี 32000 เธรดที่ใช้งานพร้อมกัน
Thorbjørn Ravn Andersen

เรากำลังสร้างไคลเอนต์ 11K ที่ใช้เธรด 32 K สำหรับการอ่านเขียนข้อมูลบนซ็อกเก็ต UDP จากเธรด 32 K เหล่านี้เธรด 10K เป็นเธรดที่มีชีวิตซึ่งใช้เพื่อให้ซ็อกเก็ตเปิดอยู่
Deepak Tewani

ฉันเชื่อว่าปัญหานี้แก้ไขได้ในเว็บเซิร์ฟเวอร์สมัยใหม่ นอกจากนี้ udp ยังทำให้แพ็คเก็ตหลวมได้ด้วยเหตุใดคุณจึงไม่ใช้แค่เว็บเซิร์ฟเวอร์?
Thorbjørn Ravn Andersen

7
เนื่องจากข้อยกเว้น OutOfMemory ควรมีชื่อว่า OutOfResources ระบบปฏิบัติการไม่สามารถจัดหาทรัพยากรที่คุณต้องการได้ (และปรากฎว่าฉันไม่รู้จัก ice4j)
Thorbjørn Ravn Andersen

14

ฉันพบปัญหาเดียวกันในระหว่างการทดสอบการโหลดสาเหตุเป็นเพราะ JVM ไม่สามารถสร้างเธรด Java ใหม่เพิ่มเติมได้ ด้านล่างนี้คือซอร์สโค้ด JVM

if (native_thread->osthread() == NULL) {    
// No one should hold a reference to the 'native_thread'.    
    delete native_thread;   
if (JvmtiExport::should_post_resource_exhausted()) {      
    JvmtiExport::post_resource_exhausted(        
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | 
        JVMTI_RESOURCE_EXHAUSTED_THREADS, 
        "unable to create new native thread");    
    } THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "unable to create new native thread");  
} Thread::start(native_thread);`

สาเหตุหลัก: JVM แสดงข้อยกเว้นนี้เมื่อ JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR (ทรัพยากรหมด (หมายถึงหน่วยความจำหมด)) หรือ JVMTI_RESOURCE_EXHAUSTED_THREADS (เธรดหมด)

ในกรณีของฉัน Jboss สร้างเธรดมากเกินไปเพื่อตอบสนองคำขอ แต่เธรดทั้งหมดถูกบล็อก ด้วยเหตุนี้ JVM จึงหมดไปพร้อมกับเธรดและหน่วยความจำ (แต่ละเธรดมีหน่วยความจำซึ่งไม่ได้ปล่อยเนื่องจากแต่ละเธรดถูกบล็อก)

วิเคราะห์การทิ้งเธรด java ที่พบว่าเธรดเกือบ 61K ถูกบล็อกโดยวิธีการหนึ่งของเราซึ่งเป็นสาเหตุของปัญหานี้ ด้านล่างนี้คือส่วนของการถ่ายโอนข้อมูลเธรด

"SimpleAsyncTaskExecutor-16562" #38070 prio=5 os_prio=0 tid=0x00007f9985440000 nid=0x2ca6 waiting for monitor entry [0x00007f9d58c2d000]
   java.lang.Thread.State: BLOCKED (on object monitor)

วิธีการบล็อกเป็นอย่างไร? ไม่กลับมา?
Thorbjørn Ravn Andersen

8

เป็นไปได้ว่าระบบปฏิบัติการของคุณไม่อนุญาตให้มีจำนวนเธรดที่คุณพยายามสร้างหรือคุณมีขีด จำกัด ใน JVM โดยเฉพาะอย่างยิ่งถ้าเป็นตัวเลขกลมๆเช่น 32k ขีด จำกัด ประเภทใดประเภทหนึ่งเป็นตัวการที่น่าจะเป็นตัวการ

แน่ใจหรือว่าต้องการเธรด 32k อย่างแท้จริง ภาษาสมัยใหม่ส่วนใหญ่มีการรองรับพูลเธรดที่ใช้ซ้ำได้ - ฉันแน่ใจว่า Java มีบางอย่างอยู่ด้วยเช่นกัน (เช่นExecutorServiceตามที่ผู้ใช้ Jesper กล่าวถึง) บางทีคุณอาจขอเธรดจากพูลดังกล่าวแทนที่จะสร้างใหม่ด้วยตนเอง


1
ขอบคุณสำหรับการตอบกลับเราใช้ไลบรารีโอเพ่นซอร์ส ICE4j และพยายามโหลดการทดสอบนั้น เราไม่สามารถเพิ่มขีด จำกัด ของเธรดใน OS ได้เมื่อเรารู้ว่ามีหน่วยความจำเหลืออยู่ 50% บนเซิร์ฟเวอร์
Deepak Tewani

1
เรากำลังสร้างไคลเอนต์ 11K ที่ใช้เธรด 32 K สำหรับการอ่านเขียนข้อมูลบนซ็อกเก็ต UDP จากเธรด 32 K เหล่านี้เธรด 10K เป็นเธรดที่มีชีวิตซึ่งใช้เพื่อให้ซ็อกเก็ตเปิดอยู่
Deepak Tewani

7

ฉันอยากจะแนะนำให้ดูที่ขนาดเธรดสแต็กและดูว่าคุณได้สร้างเธรดเพิ่มเติมหรือไม่ ขนาด Thread Stack เริ่มต้นสำหรับJRockit 1.5 / 1.6คือ 1 MB สำหรับ 64-bit VM บน Linux OS เธรด 32K จะต้องใช้หน่วยความจำกายภาพและหน่วยความจำเสมือนจำนวนมากเพื่อให้เป็นไปตามข้อกำหนดนี้

ลองลดขนาดกองเป็น512 KBเพื่อเป็นจุดเริ่มต้นและดูว่าจะช่วยสร้างเธรดเพิ่มเติมสำหรับแอปพลิเคชันของคุณได้หรือไม่ ฉันขอแนะนำให้สำรวจการปรับขนาดแนวนอนเช่นการแยกการประมวลผลแอปพลิเคชันของคุณในเครื่องจริงหรือเสมือน

เมื่อใช้ VM 64 บิตขีด จำกัด ที่แท้จริงจะขึ้นอยู่กับความพร้อมใช้งานของหน่วยความจำกายภาพและหน่วยความจำเสมือนของระบบปฏิบัติการและพารามิเตอร์การปรับแต่งระบบปฏิบัติการเช่น ulimitc ฉันขอแนะนำบทความต่อไปนี้เป็นข้อมูลอ้างอิง:

OutOfMemoryError: ไม่สามารถสร้างเธรดเนทีฟใหม่ได้ - ปัญหา Demystified


5

หาก jvm เริ่มต้นผ่าน systemd อาจมี maxTasks ต่อขีด จำกัด ของกระบวนการ (งานจริงหมายถึงเธรด) ใน linux OS บางระบบ

คุณสามารถตรวจสอบได้โดยเรียกใช้ "สถานะบริการ" และตรวจสอบว่ามีขีด จำกัด maxTasks หรือไม่ หากมีคุณสามารถลบออกได้โดยแก้ไข /etc/systemd/system.conf เพิ่ม config: DefaultTasksMax = infinity


3

ฉันมีปัญหาเดียวกันเนื่องจากกระบวนการโกสต์ที่ไม่ปรากฏขึ้นเมื่อใช้ top in bash สิ่งนี้ป้องกันไม่ให้ JVM เกิดเธรดมากขึ้น

สำหรับฉันมันได้รับการแก้ไขเมื่อแสดงรายการกระบวนการ java ทั้งหมดด้วยjps (เพียงดำเนินการjpsในเชลล์ของคุณ) และฆ่าแยกกันโดยใช้kill -9 pidคำสั่ง bash สำหรับแต่ละกระบวนการโกสต์

ซึ่งอาจช่วยได้ในบางสถานการณ์


2

คุณมีโอกาสเผชิญหน้าjava.lang.OutOfMemoryError: Unable to create new native threadเมื่อใดก็ตามที่ JVM ขอเธรดใหม่จาก OS เมื่อใดก็ตามที่ OS พื้นฐานไม่สามารถจัดสรรเธรดเนทีฟใหม่ OutOfMemoryError นี้จะถูกโยนทิ้ง ขีด จำกัด ที่แน่นอนสำหรับเธรดเนทีฟนั้นขึ้นอยู่กับแพลตฟอร์มเป็นอย่างมากดังนั้นขอแนะนำให้ค้นหาขีด จำกัด เหล่านั้นโดยเรียกใช้การทดสอบที่คล้ายกับตัวอย่างลิงก์ด้านล่าง แต่โดยทั่วไปแล้วสถานการณ์ที่ก่อให้เกิดjava.lang.OutOfMemoryError: Unable to create new native threadจะต้องดำเนินไปตามขั้นตอนต่อไปนี้:

  1. เธรด Java ใหม่ถูกร้องขอโดยแอ็พพลิเคชันที่รันภายใน JVM
  2. JVM เนทีฟโค้ดพร็อกซีขอให้สร้างเธรดเนทีฟใหม่ไปยัง OS ระบบปฏิบัติการพยายามสร้างเธรดเนทีฟใหม่ซึ่งต้องใช้หน่วยความจำเพื่อจัดสรรให้กับเธรด
  3. ระบบปฏิบัติการจะปฏิเสธการจัดสรรหน่วยความจำเนทีฟเนื่องจากขนาดกระบวนการ Java 32 บิตหมดพื้นที่แอดเดรสหน่วยความจำเช่นขีด จำกัด ขนาดกระบวนการ (2-4) GB หรือหน่วยความจำเสมือนของระบบปฏิบัติการหมดลงอย่างสมบูรณ์
  4. java.lang.OutOfMemoryError: ไม่สามารถสร้างข้อผิดพลาดเธรดเนทีฟใหม่ได้ถูกโยนทิ้ง

อ้างอิง: https://plumbr.eu/outofmemoryerror/unable-to-create-new-native-thread


2

หากต้องการค้นหาว่ากระบวนการใดกำลังสร้างเธรดให้ลอง:

ps huH

โดยปกติฉันเปลี่ยนเส้นทางเอาต์พุตไปยังไฟล์และวิเคราะห์ไฟล์แบบออฟไลน์ (การนับเธรดสำหรับแต่ละกระบวนการเป็นไปตามที่คาดไว้หรือไม่)


1

หากงานของคุณล้มเหลวเนื่องจาก OutOfMemmory บนโหนดคุณสามารถเพิ่มแผนที่และตัวลดจำนวนสูงสุดของคุณได้สองเท่าและ JVM จะเลือกสำหรับแต่ละรายการ mapred.child.java.opts (ค่าเริ่มต้นคือ 200Xmx) มักจะต้องเพิ่มขึ้นตามฮาร์ดแวร์เฉพาะโหนดข้อมูลของคุณ

ลิงค์นี้อาจเป็นประโยชน์ ... โปรดตรวจสอบ


1
เราได้ลองใช้การเปลี่ยนแปลงทั้งหมดที่ให้ไว้ในลิงค์นั้นแล้ว แต่ผลลัพธ์ก็เหมือนเดิม :(
Deepak Tewani

1

การกำหนดค่า JBoss ของคุณมีปัญหาบางอย่าง /opt/jrockit-jdk1.6/bin/java -Xms512m -Xmx512m Xms และ Xmx กำลัง จำกัด การใช้หน่วยความจำ JBoss ของคุณเป็นค่าที่กำหนดไว้ดังนั้นจาก 8Gb คุณมีเซิร์ฟเวอร์ใช้ 512M เท่านั้น + พิเศษบางอย่างเพื่อจุดประสงค์ของเขาเองเพิ่มจำนวนนั้นอย่าลืมปล่อยให้ระบบปฏิบัติการและสิ่งอื่น ๆ ทำงานอยู่ที่นั่นฟรีและอาจทำให้คุณใช้งานได้แม้จะไม่มีรหัสที่ไม่น่าพอใจ การแก้ไขโค้ดก็จะดีเช่นกันถ้าคุณทำได้


1

ข้อผิดพลาดนี้เกิดขึ้นได้เนื่องจากสาเหตุสองประการต่อไปนี้:

  • ไม่มีที่ว่างในหน่วยความจำเพื่อรองรับเธรดใหม่

  • จำนวนเธรดเกินขีด จำกัด ของระบบปฏิบัติการ

ฉันสงสัยว่าจำนวนเธรดเกินขีด จำกัด สำหรับกระบวนการ java

ดังนั้นโอกาสที่ปัญหาอาจเป็นเพราะหน่วยความจำประเด็นหนึ่งที่ควรพิจารณาคือ

เธรดไม่ได้ถูกสร้างขึ้นภายในฮีพ JVM พวกเขาถูกสร้างขึ้นนอกฮีป JVM ดังนั้นหากมีพื้นที่เหลือน้อยใน RAM หลังจากการจัดสรรฮีป JVM แอปพลิเคชันจะทำงานใน“ java.lang.OutOfMemoryError: ไม่สามารถสร้างเธรดเนทีฟใหม่ได้”

วิธีแก้ไขที่เป็นไปได้คือการลดหน่วยความจำฮีปหรือเพิ่มขนาดของหน่วยความจำโดยรวม


0

ฉันมีปัญหาเดียวกันนี้และกลายเป็นการใช้ java API ที่ไม่เหมาะสม ฉันกำลังเริ่มต้นตัวสร้างในวิธีการประมวลผลแบบแบตช์ซึ่งไม่ควรเริ่มต้นมากกว่าหนึ่งครั้ง

โดยทั่วไปฉันกำลังทำบางสิ่งเช่น:

for (batch in batches) {
    process_batch(batch)
}

def process_batch(batch) {
    var client = TransportClient.builder().build()
    client.processList(batch)
}

เมื่อฉันควรทำสิ่งนี้:

for (batch in batches) {
    var client = TransportClient.builder().build()
    process_batch(batch, client)
}

def process_batch(batch, client) {
    client.processList(batch)
}

-4

ก่อนอื่นผมจะไม่โทษว่าระบบปฏิบัติการ / VM .. ค่อนข้างนักพัฒนาที่เขียนรหัสที่สร้างกระทู้หลาย sooo โดยทั่วไปอยู่ที่ไหนสักแห่งในรหัสของคุณ (หรือบุคคลที่ 3) จำนวนมากของหัวข้อที่ถูกสร้างขึ้นโดยไม่มีการควบคุม

ตรวจสอบ stacktraces / code อย่างรอบคอบและควบคุมจำนวนเธรดที่สร้างขึ้น โดยปกติแอปของคุณไม่ควรต้องการเธรดจำนวนมากหากเป็นปัญหาอื่น


10
นี่ไม่ใช่วิธีแก้ปัญหาสำหรับคำถาม
ftrujillo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.