วิธีค้นหา Java Memory Leak


142

คุณจะพบหน่วยความจำรั่วใน Java ได้อย่างไร (โดยใช้ตัวอย่างเช่น JHat) ฉันพยายามโหลด heap dump ใน JHat เพื่อดูพื้นฐาน อย่างไรก็ตามฉันไม่เข้าใจว่าฉันควรจะสามารถค้นหาการอ้างอิงราก ( ref ) หรืออะไรก็ตามที่ถูกเรียก โดยทั่วไปฉันสามารถบอกได้ว่ามีรายการตารางแฮชหลายร้อยเมกะไบต์ ([java.util.HashMap $ Entry หรืออะไรทำนองนั้น) แต่มีการใช้แผนที่ทั่วสถานที่ ... มีวิธีการค้นหาแผนที่ขนาดใหญ่ไหม หรืออาจจะหารากทั่วไปของต้นไม้วัตถุขนาดใหญ่?

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

นอกจากนี้ฉันไม่คิดว่าฉันจะสามารถแก้ไขได้จริง (เพิ่มการบันทึกทุกขนาดแผนที่) และเรียกใช้มันนานพอที่ฉันจะสังเกตเห็นการรั่วไหล


นี่คือ "โหวต" อีกครั้งสำหรับ JProfiler มันใช้งานได้ดีสำหรับการวิเคราะห์ฮีปมี UI ที่เหมาะสมและใช้งานได้ดี ดังที่ McKenzieG1 พูดไว้ว่า $ 500 นั้นถูกกว่าระยะเวลาที่คุณจะเผาค้นหาแหล่งที่มาของรอยรั่วเหล่านี้ เท่าที่ราคาของเครื่องมือไปมันก็ไม่เลว
joev

Oracle มีหน้าเว็บที่เกี่ยวข้องที่นี่: docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/…
ลอเรล

คำตอบ:


126

ฉันใช้วิธีการดังต่อไปนี้เพื่อค้นหาหน่วยความจำรั่วใน Java ฉันใช้ jProfiler ด้วยความสำเร็จที่ยิ่งใหญ่ แต่ฉันเชื่อว่าเครื่องมือพิเศษใด ๆ ที่มีความสามารถในการสร้างกราฟ (ความแตกต่างง่ายต่อการวิเคราะห์ในรูปแบบกราฟิก) จะทำงานได้

  1. เริ่มต้นแอปพลิเคชันและรอจนกว่าจะถึงสถานะ "เสถียร" เมื่อการเริ่มต้นทั้งหมดเสร็จสมบูรณ์และแอปพลิเคชันไม่ได้ทำงาน
  2. เรียกใช้การดำเนินการที่สงสัยว่าก่อให้เกิดการรั่วไหลของหน่วยความจำหลายครั้งเพื่อให้แคชใด ๆ การเริ่มต้นที่เกี่ยวข้องกับฐานข้อมูลจะเกิดขึ้น
  3. เรียกใช้ GC และทำการจับภาพหน่วยความจำ
  4. เรียกใช้การดำเนินการอีกครั้ง ขึ้นอยู่กับความซับซ้อนของการดำเนินงานและขนาดของข้อมูลที่ถูกประมวลผลการดำเนินการอาจจำเป็นต้องเรียกใช้หลายต่อหลายครั้ง
  5. เรียกใช้ GC และทำการจับภาพหน่วยความจำ
  6. เรียกใช้ diff สำหรับ 2 snapshots และวิเคราะห์

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

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

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


7
ตัวสร้างโปรไฟล์ Java ส่วนใหญ่ (ถ้าไม่ใช่ทั้งหมด) มีตัวเลือกให้คุณเรียกใช้ GC ด้วยการคลิกปุ่ม หรือคุณสามารถโทรหา System.gc () จากที่ที่เหมาะสมในรหัสของคุณ
Dima Malenko

3
แม้ว่าเราจะเรียก System.gc () JVM อาจเลือกที่จะไม่รับสาย AFAIK นี่คือ JVM เฉพาะ +1 กับคำตอบ
Aniket Thakur

4
"ภาพรวมหน่วยความจำคืออะไร" มีบางอย่างที่จะบอกจำนวนประเภทของวัตถุแต่ละประเภทที่รหัสของฉันกำลังทำงานอยู่หรือไม่
gnomed

2
ฉันจะเริ่มจาก "เริ่มต้นจากผลต่างที่ดีที่สุดตามประเภทของวัตถุ" เป็น "ค้นหาสิ่งที่ทำให้วัตถุพิเศษเหล่านั้นติดอยู่ในหน่วยความจำ" ได้อย่างไร ฉันเห็นสิ่งทั่วไปมาก ๆ เช่น int [], Object [], String ฯลฯ ฉันจะหาที่มาได้อย่างไร
Vituel

48

ผู้ถามที่นี่ฉันต้องบอกว่าการใช้เครื่องมือที่ไม่ต้องใช้เวลา 5 นาทีในการตอบคลิกใด ๆ ทำให้การค้นหาหน่วยความจำรั่วไหลได้ง่ายขึ้น

เนื่องจากผู้คนแนะนำเครื่องมือหลายอย่าง (ฉันลองใช้ visual wm ตั้งแต่ฉันได้รับในรุ่นทดลองของ JDK และ JProbe) ฉันถึงแม้ว่าฉันควรจะแนะนำเครื่องมือโอเพนซอร์ซ / โอเพนซอร์สที่สร้างบนแพลตฟอร์ม Eclipse ตัววิเคราะห์หน่วยความจำ วิเคราะห์) ที่มีอยู่บนhttp://www.eclipse.org/mat/

สิ่งที่เจ๋งจริงๆเกี่ยวกับเครื่องมือนี้คือมันทำดัชนีดัมพ์ฮีปเมื่อฉันเปิดครั้งแรกซึ่งอนุญาตให้แสดงข้อมูลเช่นฮีปสะสมโดยไม่ต้องรอ 5 นาทีสำหรับแต่ละวัตถุ (การดำเนินการทั้งหมดค่อนข้างเร็วกว่าเครื่องมืออื่น ๆ ที่ฉันลอง) .

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


1
มูลค่า noting: เห็นได้ชัดใน Java 5 และข้างต้นHeapDumpOnCtrlBreakพารามิเตอร์ VM ไม่สามารถใช้ได้ วิธีแก้ปัญหาที่ฉันพบ (จนถึงตอนนี้ยังคงมองหา) คือการใช้ JMap เพื่อถ่ายโอน.hprofไฟล์ซึ่งฉันใส่ลงใน Eclipse และใช้ MAT เพื่อตรวจสอบ
Ben

1
เกี่ยวกับการขอรับ heap dump, profilers ส่วนใหญ่ (รวมถึง JVisualVM) รวมถึงตัวเลือกในการถ่ายโอนข้อมูลทั้ง heap และ threads ไปยังไฟล์
bbaja42

13

เครื่องมือช่วยได้มาก

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

ในกรณีนั้นจะช่วยให้คุณทราบวิธีการรอบ ๆ แฟ้มการถ่ายโอนข้อมูล hprof

มองหาเว็บไซต์เริ่มต้น สิ่งนี้แสดงให้คุณเห็นว่าวัตถุใดที่ใช้หน่วยความจำมากที่สุด แต่วัตถุจะไม่ถูกรวมเข้าด้วยกันเพียงอย่างเดียวตามประเภท: แต่ละรายการยังมี ID "ติดตาม" จากนั้นคุณสามารถค้นหา "TRACE nnnn" เพื่อดูเฟรมด้านบนสุดของสแต็กที่มีการจัดสรรวัตถุ บ่อยครั้งที่ฉันเห็นว่ามีการจัดสรรวัตถุฉันพบข้อบกพร่องและฉันทำเสร็จแล้ว นอกจากนี้โปรดทราบว่าคุณสามารถควบคุมจำนวนเฟรมที่บันทึกในสแต็กด้วยตัวเลือกเพื่อ -Xrunhprof

หากคุณตรวจสอบไซต์การจัดสรรและไม่เห็นสิ่งใดผิดคุณจะต้องเริ่มการโยงย้อนกลับจากวัตถุสดเหล่านั้นไปยังวัตถุรากเพื่อค้นหาสายอ้างอิงที่ไม่คาดคิด นี่คือที่เครื่องมือช่วยจริงๆ แต่คุณสามารถทำสิ่งเดียวกันด้วยมือ (ดีกับ grep) ไม่มีวัตถุรากเดียว (เช่นวัตถุที่ไม่ได้อยู่ภายใต้การรวบรวมขยะ) เธรดคลาสและเฟรมสแต็กทำหน้าที่เป็นวัตถุรูทและสิ่งที่อ้างอิงอย่างยิ่งนั้นไม่สามารถรวบรวมได้

หากต้องการทำการโยงให้ดูในส่วนการถ่ายโอนข้อมูลกองสำหรับรายการที่มี id การติดตามที่ไม่ดี สิ่งนี้จะนำคุณไปยังรายการ OBJ หรือ ARR ซึ่งแสดงตัวระบุวัตถุที่ไม่ซ้ำกันในฐานสิบหก ค้นหารหัสทั้งหมดที่เกิดขึ้นเพื่อค้นหาว่าใครมีการอ้างอิงที่แข็งแกร่งกับวัตถุ ปฏิบัติตามเส้นทางเหล่านั้นย้อนหลังในขณะที่พวกเขาสาขาจนกว่าคุณจะคิดออกว่ามีการรั่วไหล มาดูกันว่าทำไมเครื่องมือถึงมีประโยชน์?

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


“ ดัมพ์ฮีปมีขนาดใหญ่มากทำให้เครื่องมือขัดข้อง” - สุดท้ายฉันตรวจสอบjhatและMATพยายามโหลดเทกองฮีปทั้งหมดลงในหน่วยความจำและโดยทั่วไปแล้วมีปัญหากับการOutOfMemoryErrorทิ้งขยะขนาดใหญ่ (เช่นจากแอปพลิเคชันที่ต้องการวิเคราะห์ฮีปมากที่สุด! ) NetBeans Profiler ดูเหมือนว่าจะใช้อัลกอริทึมที่แตกต่างกันสำหรับการจัดทำดัชนีอ้างอิงซึ่งอาจทำให้การถ่ายโอนข้อมูลขนาดใหญ่ช้าลงแต่อย่างน้อยก็ไม่ได้ใช้หน่วยความจำที่ไม่ได้ จำกัด ในเครื่องมือและเกิดความผิดพลาด
Jesse Glick

10

ส่วนใหญ่แล้วในแอพพลิเคชั่นระดับองค์กรนั้น Java heap ที่ให้นั้นมีขนาดใหญ่กว่าขนาดสูงสุด 12 ถึง 16 GB ฉันพบว่ามันยากที่จะทำให้ NetBeans profiler ทำงานโดยตรงบนแอป java ขนาดใหญ่เหล่านี้

แต่โดยปกติแล้วสิ่งนี้ไม่จำเป็น คุณสามารถใช้ยูทิลิตี jmap ที่มาพร้อมกับ jdk เพื่อถ่ายโอนข้อมูลฮีป "สด" นั่นคือ jmap จะดัมพ์ฮีปหลังจากเรียกใช้ GC ทำการดำเนินการบางอย่างกับแอปพลิเคชันรอจนกว่าการดำเนินการจะเสร็จสิ้นจากนั้นใช้การถ่ายโอนข้อมูลฮีป "สด" อีกครั้ง ใช้เครื่องมือเช่น Eclipse MAT เพื่อโหลด heapdumps จัดเรียงบนฮิสโตแกรมดูว่ามีวัตถุใดเพิ่มขึ้นหรือสูงสุดใดซึ่งจะให้เบาะแส

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

มีปัญหาเดียวกับวิธีนี้คือ กองขนาดใหญ่ทิ้งแม้มีตัวเลือกสดอาจใหญ่เกินไปที่จะถ่ายโอนไปยังรอบการพัฒนาและอาจต้องใช้เครื่องที่มีหน่วยความจำ / RAM เพียงพอที่จะเปิด

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

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

แทนที่จะเก็บฮีปสองกองใช้ฮิสโทแกรมระดับสองดังที่อธิบายไว้ข้างต้น จากนั้นเปรียบเทียบฮิสโตแกรมของชั้นเรียนและดูชั้นเรียนที่เพิ่มขึ้น ดูว่าคุณสามารถเชื่อมโยงคลาส Java กับคลาสแอ็พพลิเคชันของคุณหรือไม่ นี่จะให้คำแนะนำที่ดีงาม นี่คือสคริปต์ pythons ที่สามารถช่วยคุณเปรียบเทียบการทิ้งค่าฮิสโตแกรม jmap สองรายการ histogramparser.py

ในที่สุดเครื่องมือเช่น JConolse และ VisualVm มีความสำคัญต่อการดูการเติบโตของหน่วยความจำเมื่อเวลาผ่านไปและดูว่ามีหน่วยความจำรั่ว ในที่สุดบางครั้งปัญหาของคุณอาจไม่ใช่การรั่วไหลของหน่วยความจำ แต่การใช้หน่วยความจำสูงสำหรับการเปิดใช้งานการบันทึก GC ใช้ GC ที่มีความก้าวหน้าและกระชับใหม่เช่น G1GC; และคุณสามารถใช้เครื่องมือ jdk เช่น jstat เพื่อดูพฤติกรรมของ GC แบบสด

jstat -gccause pid <optional time interval>

การอ้างอิงอื่น ๆ ไปยัง google สำหรับ -jhat, jmap, GC เต็มรูปแบบ, การจัดสรรแบบ Humongous, G1GC


1
เพิ่มโพสต์บล็อกพร้อมรายละเอียดเพิ่มเติมที่นี่ - alexpunnen.blogspot.in/2015/06/…
Alex Punnen

5

มีเครื่องมือที่จะช่วยคุณค้นหาการรั่วไหลของคุณเช่น JProbe, YourKit, AD4J หรือ JRockit Mission Control สุดท้ายคือสิ่งที่ฉันรู้จักดีที่สุดเป็นการส่วนตัว เครื่องมือที่ดีใด ๆ ที่ควรให้คุณเจาะลึกถึงระดับที่คุณสามารถระบุการรั่วไหลและการจัดสรรวัตถุที่รั่วได้ง่าย

การใช้ HashTables, Hashmaps หรือคล้ายกันเป็นหนึ่งในไม่กี่วิธีที่คุณสามารถรั่วไหลของหน่วยความจำใน Java ได้เลย ถ้าฉันต้องหารอยรั่วด้วยมือฉันจะพิมพ์ขนาด HashMaps ของฉันออกไปอย่างน่ากลัวและจากนั้นหาที่ที่ฉันเพิ่มรายการและลืมที่จะลบออก


4

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



0

คุณต้องใช้ตัวสร้างโปรไฟล์หน่วยความจำที่ติดตามการจัดสรรจริงๆ ลองดูที่JProfiler - คุณสมบัติ "ฮีปวอล์คเกอร์" ของพวกเขานั้นยอดเยี่ยมมากและพวกเขาได้ทำงานร่วมกับ Java IDE หลัก ๆ ทั้งหมด มันไม่ฟรี แต่มันก็ไม่แพงเลย ($ 499 สำหรับสิทธิ์ใช้งานเดียว) - คุณจะเผาไหม้มูลค่า $ 500 เวลาสวยดิ้นรนอย่างรวดเร็วเพื่อหารอยรั่วด้วยเครื่องมือที่ซับซ้อนน้อยกว่า


0

คุณสามารถค้นหาโดยการวัดขนาดการใช้หน่วยความจำหลังจากเรียกตัวรวบรวมขยะหลายครั้ง:

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

หากจำนวนเอาต์พุตมีค่าเท่ากันจะไม่มีการรั่วไหลของหน่วยความจำในแอปพลิเคชันของคุณ แต่ถ้าคุณเห็นความแตกต่างระหว่างจำนวนการใช้หน่วยความจำ (ตัวเลขที่เพิ่มขึ้น) แสดงว่ามีหน่วยความจำรั่วไหลในโครงการของคุณ ตัวอย่างเช่น:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

โปรดทราบว่าบางครั้งอาจต้องใช้เวลาสักครู่ในการปล่อยหน่วยความจำโดยการกระทำบางอย่างเช่นสตรีมและซ็อกเก็ต คุณไม่ควรตัดสินด้วยผลลัพธ์แรกคุณควรทดสอบในเวลาที่กำหนด


0

ชำระเงินที่หน้าจอนี้เกี่ยวกับการค้นหาการรั่วไหลของหน่วยความจำด้วย JProfiler มันเป็นคำอธิบายภาพของ @Dima Malenko Answer

หมายเหตุ: แม้ว่า JProfiler จะไม่ใช่ฟรีแวร์ แต่เวอร์ชั่นทดลองสามารถจัดการกับสถานการณ์ปัจจุบันได้


0

เนื่องจากเราส่วนใหญ่ใช้ Eclipse อยู่แล้วสำหรับการเขียนรหัสทำไมไม่ใช้ Memory Analyzer Tool (MAT) ใน Eclipse มันใช้งานได้ดี

คราส MATเป็นชุดของปลั๊กอินสำหรับ Eclipse IDE ซึ่งมีเครื่องมือในการวิเคราะห์heap dumpsจากโปรแกรม Java และระบุmemory problemsในใบสมัคร

สิ่งนี้จะช่วยให้นักพัฒนาซอฟต์แวร์ค้นหาหน่วยความจำรั่วด้วยคุณสมบัติต่อไปนี้

  1. การรับภาพรวมหน่วยความจำ (Heap Dump)
  2. histogram
  3. สะสมฮีป
  4. ต้นไม้ Dominator
  5. สำรวจเส้นทางสู่ GC Roots
  6. ผู้ตรวจการ
  7. การต่อต้านรูปแบบหน่วยความจำทั่วไป
  8. ภาษาของแบบสอบถาม

ป้อนคำอธิบายรูปภาพที่นี่

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