หน่วยความจำในกระบวนการจาวาคืออะไร?


20

เราพยายามตรวจสอบการใช้หน่วยความจำของกระบวนการ Java ภายใต้การโหลดปานกลาง

  PID   USER    PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  12663 test    20   0 8378m 6.0g 4492 S   43  8.4 162:29.95 java

อย่างที่คุณเห็นเรามีหน่วยความจำภายในขนาด 6Gb ตอนนี้ส่วนที่น่าสนใจคือ: กระบวนการถูกดำเนินการกับ params เหล่านี้:

  • -Xmx2048m
  • -Xms2048m
  • -XX: NewSize = 512m
  • -XX: MaxDirectMemorySize = 256m
  • ... บางรายการสำหรับ GC และรายการอื่น ๆ

เมื่อดูการตั้งค่าเหล่านี้และการใช้งานหน่วยความจำจริงเราจะสะดุดเพื่อดูความแตกต่างของสิ่งที่เราคาดว่ากระบวนการนี้จะใช้และสิ่งที่ใช้จริง

โดยปกติแล้วปัญหาหน่วยความจำของเราจะได้รับการแก้ไขโดยการวิเคราะห์การถ่ายโอนข้อมูลกอง แต่ในกรณีนี้หน่วยความจำของเราจะใช้ที่ไหนสักแห่งนอกกอง

คำถาม: ขั้นตอนในการลองและค้นหาสาเหตุของการใช้หน่วยความจำสูงเช่นไร? เครื่องมือใดที่สามารถช่วยเราระบุสิ่งที่ใช้หน่วยความจำในกระบวนการนั้น

แก้ไข 0

ดูเหมือนว่านี่จะเป็นปัญหาที่เกี่ยวข้องกับกองเนื่องจากเรายังมีพื้นที่ค่อนข้างบาง:

jmap -heap 12663

ผลลัพธ์ใน (แก้ไขเพื่อประหยัดพื้นที่)

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize      = 2147483648 (2048.0MB)
NewSize          = 536870912 (512.0MB)
MaxNewSize       = 536870912 (512.0MB)
OldSize          = 1610612736 (1536.0MB)
NewRatio         = 7
SurvivorRatio    = 8
PermSize         = 21757952 (20.75MB)
MaxPermSize      = 85983232 (82.0MB)

New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used

แก้ไข 1

ใช้ pmap เราจะเห็นว่ามีการจัดสรร 64Mb ค่อนข้าง:

pmap -x 12663 | grep rwx | sort -n -k3 | less

ผลลัพธ์ใน:

... a lot more of these 64Mb chunks
00007f32b8000000       0   65508   65508 rwx--    [ anon ] <- what are these?
00007f32ac000000       0   65512   65512 rwx--    [ anon ]
00007f3268000000       0   65516   65516 rwx--    [ anon ]
00007f3324000000       0   65516   65516 rwx--    [ anon ]
00007f32c0000000       0   65520   65520 rwx--    [ anon ]
00007f3314000000       0   65528   65528 rwx--    [ anon ] 
00000000401cf000       0  241904  240980 rwx--    [ anon ] <- Direct memory ?
000000077ae00000       0 2139688 2139048 rwx--    [ anon ] <- Heap ?

ดังนั้นวิธีการค้นหาชิ้น 64Mb เหล่านั้นคืออะไร ใช้อะไร มีข้อมูลประเภทใดอยู่ในนั้น

ขอบคุณ


2
ฉันได้รับปัญหาเดียวกันทั้งหมด ... นี่คือคำถามของฉัน stackoverflow.com/questions/18734389/…คุณมีทางออกเกี่ยวกับเรื่องนี้หรือไม่?
— DeepNightTwo

คำตอบ:


21

ปัญหาที่อาจจะเกี่ยวข้องกับเรื่องนี้ปัญหา glibc

โดยทั่วไปเมื่อคุณมีหลายเธรดที่จัดสรรหน่วยความจำ glibc จะเพิ่มจำนวนของจำนวนที่พร้อมใช้งานที่จะทำการจัดสรรจากเพื่อหลีกเลี่ยงการล็อกการโต้แย้ง สนามกีฬามีขนาดใหญ่ 64Mb ขีด จำกัด บนคือการสร้าง 8 เท่าของจำนวนแกนกลางโดยสิ้นเชิง Arenas จะถูกสร้างขึ้นตามความต้องการเมื่อเธรดเข้าถึงเวทีที่ถูกล็อกไว้แล้วเพื่อให้เติบโตตามเวลา

ใน Java ที่ซึ่งคุณโรยด้วยเธรดสิ่งนี้สามารถนำไปสู่การสร้างมากมายโดยสิ้นเชิง และมีการจัดสรรกระจายไปทั่วเวทีเหล่านี้ เริ่มต้นแต่ละเวที 64Mb ถูกแมปหน่วยความจำที่ไม่ได้รับการแมป แต่เมื่อคุณทำการจัดสรรคุณจะเริ่มใช้หน่วยความจำจริงสำหรับพวกเขา

pmap ของคุณมีรายชื่อที่คล้ายกับด้านล่างนี้ สังเกตว่า 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K อย่างไร นั่นคือพวกเขารวมได้ถึง 64Mb

00007f4394000000 324K rw --- [อานนท์]
00007f4394051000 65212K ----- [anon]
00007f4398000000 560K rw --- [อานนท์]
00007f439808c000 64976K ----- [anon]
00007f439c000000 620K rw --- [อานนท์]
00007f439c09b000 64916K ----- [anon]

สำหรับวิธีแก้ปัญหา : ข้อผิดพลาดกล่าวถึงพารามิเตอร์สภาพแวดล้อมบางอย่างที่คุณสามารถตั้งค่าเพื่อ จำกัด จำนวนของเวที แต่คุณต้องมีเวอร์ชัน glibc สูงพอ


5
ตั้งค่า Xmx และ Xms เป็นค่าเดียวกันพร้อมตั้งค่าตัวแปรสภาพแวดล้อม "ส่งออก MALLOC_ARENA_MAX = 4" ในสคริปต์ sh ที่เริ่มบริการเว็บของเราช่วยในกรณีของเรา ก่อนหน้านี้เราพบว่าบริการเว็บเริ่มต้นใหม่เนื่องจาก OOM Killer ทุก 2 ถึง 8 ชั่วโมง รุ่น GLIBC ใน Ubuntu 14.04 คือ 2.19 ซึ่งดีเพราะมันต้อง> = 2.16 สำหรับการตั้งค่า MALLOC_ARENA_MAX ให้ทำงาน
— Kluyg

คำตอบนี้และความคิดเห็นข้างต้นเป็นเครื่องช่วยชีวิตสำหรับฉัน ในกรณีของฉัน MALLOC_ARENA_MAX = 1 เป็นสิ่งที่จำเป็นและมีประสิทธิภาพ
— จอห์นบากีร์

3

แล้วLamdba Probeล่ะ? เหนือสิ่งอื่นใดมันสามารถแสดงการแยกการใช้งานหน่วยความจำให้คุณคล้ายกับภาพหน้าจอด้านล่าง:

มุมมองการใช้งานหน่วยความจำ Lambda Probe

บางครั้งpmap -x your_java_pidก็มีประโยชน์เช่นกัน


ขอบคุณสำหรับคำตอบ. ถ้าฉันเข้าใจถูกต้องแลมบ์ดาโพรบสำหรับ Apache Tomcat? ซึ่งเราไม่ได้ใช้ ... เกี่ยวกับ pmap ฉันจะเพิ่มข้อมูลไปยังโพสต์ด้านบน
— Konstantin S.

2

JProfiler อาจเป็นสิ่งที่คุณแสวงหา แต่ไม่ฟรี เครื่องมือที่ดีและไม่เสียค่าใช้จ่ายในการตรวจสอบการใช้หน่วยความจำของกระบวนการจาวาคือ Java VisualVM ที่มีให้เป็นเครื่องมือ JDK ในการกระจาย Oracle / Sun JDK ฉันเองแนะนำวิธีการแบบองค์รวมมากขึ้นในการแก้ไขปัญหา (เช่นการตรวจสอบดิสก์ JDK + OS + ฯลฯ ) - การใช้ระบบการตรวจสอบเครือข่ายบางอย่าง - Nagios, Verax NMS หรือ OpenNMS


2
การรั่วไหลของเขานั้นไม่มากดังนั้น JProfiler จะไม่ช่วยที่นี่จริงๆ
— Asaf Mesika

2

ปัญหาอยู่นอกกองดังนั้นผู้สมัครที่ดีที่สุดคือ:

JNI leak  
Allocation of direct memory buffer

เนื่องจากคุณ จำกัด ขนาดบัฟเฟอร์โดยตรงผู้สมัครที่ดีที่สุดในความคิดของฉันคือ JNI รั่วไหล


1

มีเครื่องมือที่ใช้งานง่ายเพื่อดูการจัดสรรหน่วยความจำฮีปที่รวมอยู่ใน JDK ที่เรียกว่า jmap นอกเหนือจากนี้คุณยังมี stack etc (Xss) รันคำสั่ง jmap ทั้งสองนี้เพื่อรับข้อมูลเพิ่มเติมเกี่ยวกับการใช้หน่วยความจำ:

jmap -heap <PID>
jmap -permstat <PID>

เพื่อรับข้อมูลเพิ่มเติมมากขึ้นคุณสามารถเชื่อมต่อกับกระบวนการด้วย jconsole (รวมอยู่ใน JDK) Jconsole จะต้องใช้ JMX เพื่อกำหนดค่าในแอปพลิเคชัน


ขอบคุณสำหรับคำตอบของคุณ แต่ปัญหาดูเหมือนว่าจะอยู่นอกกอง ฉันจะอัปเดตโพสต์ยอดนิยมเพื่อสะท้อนข้อมูลจาก jmap
— Konstantin S.

กระบวนการมีเธรดจำนวนเท่าใด ขนาดสแต็คเป็นค่าเริ่มต้น 2MB บนแพลตฟอร์มส่วนใหญ่ดังนั้นคูณด้วยจำนวนเธรด ฉันจะแปลกใจถ้ามันคำนึงถึงหน่วยความจำ "ที่หายไป" ทั้งหมด แต่อาจจะมีบางอย่าง
— HampusLi

ประมาณ 300 เธรดนำข้อมูลจาก pmap เรามีขนาดสแต็ค 1Mb และจากเอาต์พุต pmap เดียวกันมันไม่เหมือนกับกองที่ใช้มากกว่า 100Kb
— Konstantin S.

0

ใช้ JVisualVM มันมีมุมมองที่แตกต่างหลากหลายที่จะบอกคุณว่ามีหน่วยความจำฮีปเท่าใดกำลังใช้งาน, PermGen และต่อ ๆ ไปเรื่อย ๆ

สำหรับการตอบคำถามของคุณ Java จัดการหน่วยความจำค่อนข้างแตกต่างจากสิ่งที่คุณคาดหวัง

เมื่อคุณตั้งค่าพารามิเตอร์ -Xms และ -Xmx คุณกำลังบอก JVM ว่าควรจัดสรรหน่วยความจำเท่าใดในฮีปเพื่อเริ่มต้นด้วยและควรจัดสรรเป็นจำนวนสูงสุด

หากคุณมีแอ็พพลิเคชัน java ที่ใช้หน่วยความจำทั้งหมด 1m แต่ส่งผ่านใน -Xms256m -Xmx2g ดังนั้น JVM จะเริ่มต้นตัวเองด้วยหน่วยความจำ 256m ที่ใช้ มันจะไม่ใช้น้อยกว่านั้น ไม่สำคัญว่าแอปพลิเคชันของคุณใช้หน่วยความจำเพียง 1 เมตร

ในประการที่สอง ในกรณีข้างต้นหากคุณแอพบางจุดใช้หน่วยความจำมากกว่า 256m JVM จะจัดสรรหน่วยความจำให้มากที่สุดเท่าที่จำเป็นเพื่อให้บริการตามคำขอ อย่างไรก็ตามมันจะไม่ลดขนาดฮีปกลับไปเป็นค่าต่ำสุด อย่างน้อยไม่อยู่ภายใต้สถานการณ์ส่วนใหญ่

ในกรณีของคุณเนื่องจากคุณกำลังตั้งค่าหน่วยความจำขั้นต่ำและสูงสุดให้เป็น 2g JVM จะจัดสรร 2g เมื่อเริ่มต้นและบำรุงรักษา

การจัดการหน่วยความจำ Java ค่อนข้างซับซ้อนและการปรับใช้หน่วยความจำอาจเป็นงานในตัวมันเอง อย่างไรก็ตามมีทรัพยากรมากมายที่อาจช่วยได้

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