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


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.