วิธีค้นหา 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.