สิ่งที่คุณต้องรู้ก่อน
1. โปรแกรมเมอร์ C/C++ จัดการหน่วยความจำด้วยตัวเอง ในขณะที่หน่วยความจำ Java จะถูกเรียกคืนโดยอัตโนมัติโดย GC
แม้ว่าฉันจะไม่คุ้นเคยกับ C++ มากนัก แต่ฉันก็ไม่ได้ทำผิดสามัญสำนึกในเรื่องนี้
2. หน่วยความจำรั่วคืออะไร?
หน่วยความจำรั่วหมายถึงการมีอยู่ของหน่วยความจำในระบบที่ไม่สามารถรีไซเคิลได้ ซึ่งบางครั้งทำให้หน่วยความจำไม่เพียงพอหรือระบบล่ม
ใน C/C++ หน่วยความจำรั่วเกิดขึ้นเมื่อไม่มีการจัดสรรหน่วยความจำ
3. มีหน่วยความจำรั่วใน Java เราต้องยอมรับสิ่งนี้ก่อนจึงจะสามารถพูดคุยต่อไปได้ แม้ว่า Java จะมีหน่วยความจำรั่ว แต่โดยพื้นฐานแล้วคุณไม่จำเป็นต้องสนใจมัน โดยเฉพาะผู้ที่ไม่เฉพาะเจาะจงเกี่ยวกับโค้ดเอง
หน่วยความจำรั่วใน Java หมายถึง: มีวัตถุไร้ประโยชน์ที่ตัวรวบรวมขยะไม่สามารถรีไซเคิลได้
และแม้ว่าจะมีปัญหาหน่วยความจำรั่วก็อาจไม่แสดงขึ้นมา
4. พารามิเตอร์ใน Java ทั้งหมดถูกส่งผ่านโดยค่า
โดยพื้นฐานแล้วไม่มีการคัดค้านประเภทพื้นฐาน แต่เราไม่สามารถคัดค้านประเภทการอ้างอิงได้
หน่วยความจำ Java รั่วไหล
1. หน่วยความจำฮีปล้น (outOfMemoryError: พื้นที่ฮีป java)
ในข้อกำหนด JVM หน่วยความจำในฮีปจะใช้เพื่อสร้างอินสแตนซ์ของวัตถุและอาร์เรย์
หากแบ่งย่อย ความทรงจำฮีปยังสามารถแบ่งออกเป็นรุ่นเยาว์และรุ่นเก่าได้
เมื่อมีการสร้างออบเจ็กต์ใหม่ กระบวนการแอปพลิเคชันหน่วยความจำจะเป็นดังนี้:
ก. jvm พยายามจัดสรรหน่วยความจำที่จำเป็นสำหรับอ็อบเจ็กต์ใหม่ในพื้นที่อีเดน
b. หากขนาดหน่วยความจำเพียงพอ แอปพลิเคชันจะสิ้นสุด ไม่เช่นนั้นขั้นตอนต่อไปคือ
c. JVM เริ่มต้น youngGC และพยายามปล่อยวัตถุที่ใช้งานอยู่ในพื้นที่ Eden หลังจากปล่อยแล้ว หากพื้นที่ Eden ยังไม่เพียงพอที่จะวางวัตถุใหม่ ก็จะพยายามนำวัตถุที่ใช้งานอยู่บางส่วนในพื้นที่ Eden เข้าสู่พื้นที่ Survivor
d. พื้นที่ผู้รอดชีวิตถูกใช้เป็นพื้นที่แลกเปลี่ยนระหว่าง Eden และ Old เมื่อพื้นที่ OLD มีพื้นที่เพียงพอ วัตถุในพื้นที่ Survivor จะถูกย้ายไปยังพื้นที่ Old มิฉะนั้นจะถูกเก็บไว้ในพื้นที่ Survivor
จ. เมื่อมีพื้นที่ไม่เพียงพอในพื้นที่ OLD JVM จะดำเนินการ GC เต็มรูปแบบในพื้นที่ OLD
ฉ. หลังจาก GC เต็มแล้ว หากพื้นที่ผู้รอดชีวิตและเก่ายังคงไม่สามารถจัดเก็บวัตถุบางอย่างที่คัดลอกมาจาก Eden ได้ ทำให้ JVM ไม่สามารถสร้างพื้นที่หน่วยความจำสำหรับวัตถุใหม่ในพื้นที่ Eden ได้ ข้อความ "ข้อผิดพลาดหน่วยความจำไม่เพียงพอ" จะปรากฏขึ้น:
outOfMemoryError:พื้นที่ฮีป java
2. หน่วยความจำล้นในพื้นที่วิธีการ (outOfMemoryError: พื้นที่ permgem)
ในข้อกำหนด JVM พื้นที่วิธีการจัดเก็บข้อมูลคลาส ค่าคงที่ ตัวแปรคงที่ ฯลฯ เป็นหลัก
ดังนั้น หากโปรแกรมโหลดคลาสมากเกินไป หรือใช้เทคโนโลยีการสร้างพร็อกซีแบบไดนามิก เช่น การสะท้อนกลับหรือ gclib อาจทำให้หน่วยความจำล้นในพื้นที่นี้ โดยทั่วไป ข้อความแสดงข้อผิดพลาดเมื่อหน่วยความจำล้นเกิดขึ้นในพื้นที่นี้คือ:
outOfMemoryError:เพิ่มพื้นที่
3. เธรดสแต็กล้น (java.lang.StackOverflowError)
เธรดสแต็กเป็นโครงสร้างหน่วยความจำเฉพาะของเธรด ดังนั้นปัญหาเกี่ยวกับสแต็กเธรดจะต้องเกิดข้อผิดพลาดเมื่อเธรดกำลังทำงานอยู่
โดยทั่วไป เธรดสแต็กล้นเกิดจากการเรียกซ้ำที่ลึกเกินไปหรือการเรียกเมธอดหลายระดับเกินไป
ข้อความแสดงข้อผิดพลาดเมื่อเกิดสแต็กโอเวอร์โฟลว์คือ:
ชวา หลาง ข้อผิดพลาด StackOverflow
หน่วยความจำรั่วมีหลายสถานการณ์:
1. วัตถุอายุยืนอ้างอิงถึงวัตถุอายุสั้น
นี่คือสถานการณ์ทั่วไปของหน่วยความจำรั่วและปัญหาทั่วไปในการออกแบบโค้ด
ตัวอย่างเช่น หากตัวแปรในเครื่องถูกแคชไว้ในแผนที่แบบคงที่ส่วนกลางและไม่มีการดำเนินการล้าง แผนที่จะมีขนาดใหญ่ขึ้นเรื่อยๆ เมื่อเวลาผ่านไป ส่งผลให้หน่วยความจำรั่ว
2. แก้ไขค่าพารามิเตอร์ของออบเจ็กต์ในแฮชเซ็ต และพารามิเตอร์คือฟิลด์ที่ใช้ในการคำนวณค่าแฮช
หลังจากที่วัตถุถูกเก็บไว้ในคอลเลกชัน HashSet แล้ว ฟิลด์ในวัตถุที่มีส่วนร่วมในการคำนวณค่าแฮชจะไม่สามารถแก้ไขได้ มิฉะนั้นค่าแฮชที่แก้ไขของวัตถุจะแตกต่างจากค่าแฮชเมื่อถูกเก็บไว้ในคอลเลกชัน HashSet ในตอนแรก ในกรณีนี้ แม้ว่าเมธอด contains จะใช้การอ้างอิงปัจจุบันของออบเจ็กต์เป็นพารามิเตอร์เพื่อดึงออบเจ็กต์จากคอลเลกชัน HashSet ก็จะส่งคืนผลลัพธ์ที่ไม่พบออบเจ็กต์ ซึ่งจะส่งผลให้เกิดความล้มเหลวเช่นกัน ลบวัตถุปัจจุบันออกจากคอลเลกชัน HashSet ทำให้หน่วยความจำรั่ว
3. กำหนดจำนวนการเชื่อมต่อและเวลาปิดเครื่อง
การเปิดการเชื่อมต่อที่ใช้ทรัพยากรจำนวนมากเป็นเวลานานอาจทำให้หน่วยความจำรั่วได้
มาดูตัวอย่างหน่วยความจำรั่ว:
สแต็คคลาสสาธารณะ { วัตถุส่วนตัว [] องค์ประกอบ = วัตถุใหม่ [10]; วัตถุสาธารณะป๊อป () { ถ้า ( ขนาด == 0) โยน EmptyStackException(); return element[--size]; } private void allowance(){ if(elements.length == size){ Object[] oldElements = องค์ประกอบ; องค์ประกอบ = วัตถุใหม่ [2 * องค์ประกอบ ความยาว+1]; ระบบ. arraycopy (องค์ประกอบเก่า, 0, องค์ประกอบ, 0, ขนาด); } }}
หลักการข้างต้นควรจะเรียบง่ายมาก หากมีการเพิ่มองค์ประกอบ 10 รายการลงในสแต็กแล้วทั้งหมดก็หลุดออกมา แม้ว่าสแต็กจะว่างเปล่าและไม่มีอะไรที่เราต้องการ แต่ออบเจ็กต์นี้ก็ไม่สามารถรีไซเคิลได้ ซึ่งเป็นไปตามข้อกำหนดสองประการของหน่วยความจำรั่ว สภาพสินค้า: ไร้ประโยชน์ ไม่สามารถรีไซเคิลได้
แต่การมีอยู่ของสิ่งนั้นก็อาจไม่ได้นำไปสู่ผลที่ตามมาใดๆ ทั้งสิ้น หากมีการใช้สแต็กนี้น้อยลง
มันเป็นการสิ้นเปลืองหน่วยความจำเพียงไม่กี่ K อย่างไรก็ตาม หน่วยความจำของเราขึ้นอยู่กับ G แล้ว มันจะมีผลกระทบอะไรอีก นอกจากนี้ สิ่งนี้จะถูกรีไซเคิลในไม่ช้า แล้วมันสำคัญอะไรล่ะ? ลองดูสองตัวอย่างด้านล่าง
ตัวอย่างที่ 1
คลาสสาธารณะไม่ดี { สแต็กสแตติกสาธารณะ s = สแต็ก (); สแตติก { s. ผลักดัน (วัตถุใหม่ ()); s. pop(); //มีหน่วยความจำรั่วในวัตถุที่นี่ push(new Object()); // วัตถุด้านบนสามารถนำกลับมาใช้ใหม่ได้ซึ่งเทียบเท่ากับการรักษาตัวเอง}}
เนื่องจากเป็นแบบคงที่ จึงจะอยู่ได้จนกว่าโปรแกรมจะออก แต่เราก็เห็นว่ามันมีฟังก์ชันการรักษาตัวเองด้วย
กล่าวคือ หาก Stack ของคุณมีอ็อบเจ็กต์มากที่สุด 100 รายการ จะไม่สามารถรีไซเคิลอ็อบเจ็กต์ได้มากที่สุดเพียง 100 รายการ อันที่จริง สิ่งนี้ควรจะเข้าใจได้ง่าย เพราะเมื่อเราเพิ่มความคืบหน้าใหม่ ข้อมูลอ้างอิงก่อนหน้านี้จะหายไปอย่างเป็นธรรมชาติ!
ตัวอย่างที่ 2
คลาสสาธารณะ NotTooBad { โมฆะสาธารณะทำบางอย่าง () { สแต็ค s = สแต็คใหม่ (); s push(วัตถุใหม่()); //รหัสอื่น ๆ pop();//ส่งผลให้วัตถุไม่สามารถรีไซเคิลได้และหน่วยความจำรั่ว }//ออกจากเมธอด s จะใช้ไม่ได้โดยอัตโนมัติ s สามารถรีไซเคิลได้ และการอ้างอิงภายใน Stack จะหายไปตามธรรมชาติ ดังนั้น //Self-healing ก็สามารถทำได้ที่นี่ และอาจกล่าวได้ว่าวิธีนี้ไม่มี ปัญหาหน่วยความจำรั่วแต่จะแจ้งทีหลัง// มอบให้กับ GC เท่านั้น เนื่องจากปิดและไม่เปิดให้บุคคลภายนอกสามารถพูดได้ข้างต้น รหัส 99. 9999% ของ // สถานการณ์จะไม่มีผลกระทบใดๆ แน่นอน ถ้าคุณเขียนโค้ดดังกล่าว มันจะไม่ส่งผลเสียใดๆ แต่ // เรียกได้ว่าเป็นรหัสขยะแน่นอน! ฉันจะเพิ่มอันหนึ่งเข้าไป การวนซ้ำที่ว่างเปล่าจะไม่ส่งผลกระทบใหญ่หลวงใดๆ ใช่ไหม?}
สองตัวอย่างข้างต้นเป็นเพียงเรื่องเล็กน้อย แต่หน่วยความจำรั่วใน C/C++ ไม่ได้แย่ แต่แย่ที่สุด
หากไม่รีไซเคิลในที่เดียวก็จะไม่สามารถรีไซเคิลได้ หากเรียกวิธีนี้บ่อยๆ หน่วยความจำจะหมด!
เนื่องจาก Java มีฟังก์ชันการรักษาตัวเองด้วย (ฉันตั้งชื่อมันเองและยังไม่ได้ยื่นขอรับสิทธิบัตร) ปัญหาหน่วยความจำรั่วของ Java แทบจะมองข้ามไป แต่ผู้ที่รู้ก็ไม่ควรกระทำมัน
เพื่อหลีกเลี่ยงไม่ให้หน่วยความจำรั่ว คุณสามารถอ้างอิงคำแนะนำต่อไปนี้เมื่อเขียนโค้ด:
1. เผยแพร่การอ้างอิงถึงวัตถุที่ไม่มีประโยชน์โดยเร็วที่สุด
2. ใช้การประมวลผลสตริง หลีกเลี่ยงการใช้ String และใช้ StringBuffer อย่างกว้างขวาง แต่ละออบเจ็กต์ String ต้องใช้พื้นที่หน่วยความจำที่เป็นอิสระ
3. ใช้ตัวแปรคงที่ให้น้อยที่สุดเท่าที่จะเป็นไปได้ เนื่องจากตัวแปรคงที่ถูกเก็บไว้ในรุ่นถาวร (พื้นที่วิธีการ) และรุ่นถาวรโดยพื้นฐานแล้วจะไม่มีส่วนร่วมในการรวบรวมขยะ
4. หลีกเลี่ยงการสร้างวัตถุในลูป
5. การเปิดไฟล์ขนาดใหญ่หรือการรับข้อมูลจากฐานข้อมูลมากเกินไปในคราวเดียวอาจทำให้หน่วยความจำล้นได้ง่าย ดังนั้นในตำแหน่งเหล่านี้ คุณควรคำนวณจำนวนข้อมูลสูงสุดโดยประมาณ และตั้งค่าพื้นที่หน่วยความจำขั้นต่ำและสูงสุดที่ต้องการ