ใน Java การจัดการหน่วยความจำมีสองด้าน: การจัดสรรหน่วยความจำ (เมื่อสร้างวัตถุ Java) และการรีไซเคิลหน่วยความจำ การทำงานทั้งสองด้านจะเสร็จสมบูรณ์โดย JVM โดยอัตโนมัติลดความยากลำบากในการเรียนรู้สำหรับโปรแกรมเมอร์ Java และหลีกเลี่ยงอันตรายจากการทำงานของหน่วยความจำโดยตรงเช่น C/C ++ อย่างไรก็ตามมันเป็นเพราะการจัดการหน่วยความจำได้รับการจัดการทั้งหมดโดย JVM ว่าโปรแกรมเมอร์ Java จำนวนมากไม่สนใจเกี่ยวกับการจัดสรรหน่วยความจำอีกต่อไปส่งผลให้โปรแกรมหลายโปรแกรมไม่มีประสิทธิภาพและใช้หน่วยความจำ ดังนั้นโปรแกรมเมอร์ Java ควรเข้าใจ JVM ในที่สุดเพื่อที่จะเขียนโปรแกรมที่มีประสิทธิภาพมากขึ้นและใช้ประโยชน์จากหน่วยความจำที่ จำกัด อย่างเต็มที่
1. สถานะของ Java ในความทรงจำ
ก่อนอื่นมาเขียนโค้ดเป็นตัวอย่าง:
person.java
การทดสอบแพ็คเกจ; นำเข้า java.io.serializable; บุคคลระดับสาธารณะใช้ serializable {คงที่สุดท้าย long serialversionuid = 1l; ชื่อสตริง; // ชื่อเพื่อนคน; // เพื่อนสาธารณะ () {} บุคคลสาธารณะ (ชื่อสตริง) {super (); this.name = ชื่อ; - test.java
การทดสอบแพ็คเกจ; การทดสอบระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {บุคคล p1 = บุคคลใหม่ ("เควิน"); บุคคล p2 = บุคคลใหม่ ("ฝน"); บุคคล P3 = คนใหม่ ("ซันนี่"); p1.friend = p2; P3 = P2; p2 = null; - หากคุณวาดการอ้างอิงวัตถุในแง่มุมหลักใน test.java ด้านบนลงในไดอะแกรมอ้างอิงวัตถุเริ่มต้นจากวิธีหลักมันเป็นเช่นนี้ (จุดยอดเป็นวัตถุและการอ้างอิงและขอบกำกับคือความสัมพันธ์อ้างอิง):
เมื่อโปรแกรมทำงานหลังจากที่ได้รับการยกย่องว่าเป็นกราฟกำกับมันสามารถแบ่งออกเป็นสามประเภท:
1) สถานะที่ทำได้: หลังจากสร้างวัตถุแล้วตัวแปรอ้างอิงมากกว่าหนึ่งตัวจะอ้างถึงมัน ในกราฟกำกับคุณสามารถนำทางไปยังวัตถุจากจุดสุดยอดเริ่มต้นและอยู่ในสถานะที่เข้าถึงได้
2) สถานะที่สามารถกู้คืนได้: หากวัตถุในโปรแกรมไม่มีตัวแปรอ้างอิงใด ๆ ที่อ้างถึงมันอีกต่อไปมันจะเข้าสู่สถานะที่สามารถคืนค่าได้ก่อนและในเวลานี้มันไม่สามารถนำทางไปยังวัตถุจากจุดสุดยอดเริ่มต้นของกราฟกำกับ ในสถานะนี้กลไกการรวบรวมขยะของระบบพร้อมที่จะรีไซเคิลหน่วยความจำที่ครอบครองโดยวัตถุ ก่อนที่จะรีไซเคิลระบบจะเรียกวิธีการสรุป () เพื่อทำความสะอาดทรัพยากร หากตัวแปรอ้างอิงมากกว่าหนึ่งตัวถูกเรียกคืนหลังจากการจัดเรียงทรัพยากรวัตถุจะกลายเป็นสถานะที่เข้าถึงได้อีกครั้ง มิฉะนั้นจะเข้าสู่สถานะที่ไม่สามารถเข้าถึงได้
3) สถานะที่ไม่สามารถเข้าถึงได้: เมื่อการเชื่อมโยงทั้งหมดของวัตถุถูกตัดออกและระบบเรียกวิธีการสรุป () เพื่อทำความสะอาดทรัพยากรที่ยังไม่ทำให้สถานะของวัตถุที่สามารถเข้าถึงได้จากนั้นวัตถุจะสูญเสียการอ้างอิงอย่างถาวรและกลายเป็นสถานะที่ไม่สามารถเข้าถึงได้และระบบจะรีไซเคิลทรัพยากรที่ครอบครองโดยวัตถุอย่างแท้จริง
แผนภาพการเปลี่ยนแปลงของสามรัฐข้างต้นมีดังนี้:
2. 4 การอ้างอิงถึงวัตถุโดย Java
1) การอ้างอิงที่แข็งแกร่ง: สร้างวัตถุและกำหนดวัตถุนี้โดยตรงกับตัวแปรเช่น: บุคคลบุคคล = บุคคลใหม่ ("ซันนี่"); ไม่ว่าทรัพยากรของระบบจะแน่นแค่ไหนวัตถุอ้างอิงที่แข็งแกร่งจะไม่ถูกรีไซเคิลแม้ว่าจะไม่ได้ใช้อีกครั้งในอนาคต
2) การอ้างอิงที่อ่อนนุ่ม: นำไปใช้ผ่านคลาส softreference เช่น: softreference <person> p = ใหม่ softreference <person> (คนใหม่ ("ฝน"));, เมื่อหน่วยความจำแน่นมากมันจะรีไซเคิลและจะไม่รีไซเคิลในเวลาอื่นหรือไม่ก่อนที่จะใช้
3) การอ้างอิงที่อ่อนแอ: นำไปใช้ผ่านระดับความอ่อนแอเช่น: การอ้างอิงที่อ่อนแอ <person> p = new beakreference <person> (บุคคลใหม่ ("ฝน")); ไม่ว่าหน่วยความจำจะเพียงพอหรือไม่ระบบจะถูกรีไซเคิลอย่างแน่นอนในระหว่างการรวบรวมขยะ
4) คำพูดเสมือนจริง: มันไม่สามารถใช้เพียงอย่างเดียวส่วนใหญ่จะใช้เพื่อติดตามสถานะของวัตถุที่ถูกเก็บรวบรวม ดำเนินการผ่านคลาส phantomreference และคลาสอ้างอิงคิวอ้างอิงเช่น::
การทดสอบแพ็คเกจ; นำเข้า java.lang.ref.phantomreference; นำเข้า java.lang.ref.referencequeue; การทดสอบระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {// สร้างบุคคลวัตถุ = บุคคลใหม่ ("ซันนี่"); // สร้างคิวอ้างอิงอ้างอิง <person> rq = new ReferenceQueue <person> (); // สร้างการอ้างอิงเสมือนจริงให้อ้างอิงการอ้างอิงเสมือนจริงนี้ไปยังวัตถุ phantomreference <person> pr = phantomreference ใหม่ <person> (บุคคล, RQ); // ชำระตัวแปรอ้างอิงและวัตถุของบุคคลอ้างอิงบุคคล = null; // พยายามออกวัตถุที่อ้างอิงโดยการอ้างอิงเสมือนจริง // ฉันพบว่าโปรแกรมไม่สามารถเข้าถึงวัตถุอ้างอิงผ่านการอ้างอิงเสมือนจริงดังนั้นผลลัพธ์ที่นี่คือ null system.out.println (pr.get ()); // การบังคับให้เก็บขยะ System.gc (); System.runfinolization (); // เนื่องจากเมื่อวัตถุในการอ้างอิงเสมือนถูกรีไซเคิลการอ้างอิงเสมือนจะป้อนคิวอ้างอิง // ดังนั้นให้ใช้การอ้างอิงก่อนที่จะป้อนคิวในคิวเพื่อเปรียบเทียบกับ PR และเอาต์พุต System.out.out.println (rq.poll () == pr); -ผลการทำงาน:
3. กลไกการรวบรวมขยะ Java
ในความเป็นจริงการรวบรวมขยะ Java ส่วนใหญ่ทำสองสิ่ง: 1) การรีไซเคิลหน่วยความจำ 2) defragmentation
3.1 อัลกอริทึมการรวบรวมขยะ
1) การรีไซเคิลอนุกรม (CPU เพียงหนึ่งเดียว) และการรีไซเคิลแบบขนาน (ซีพียูหลายตัวมีประโยชน์): การรีไซเคิลแบบอนุกรมหมายความว่าไม่ว่าระบบจะมีซีพียูจำนวนเท่าใดมันก็เป็นเพียงซีพียูเดียวเท่านั้นที่จะดำเนินการรวบรวมขยะ การรีไซเคิลแบบขนานหมายถึงการแยกงานรีไซเคิลทั้งหมดออกเป็นหลายส่วนแต่ละส่วนจะรับผิดชอบโดย CPU หนึ่งตัวเพื่อให้ซีพียูหลายตัวสามารถรีไซเคิลได้ในแบบคู่ขนาน การรีไซเคิลแบบขนานนั้นมีประสิทธิภาพมากในการดำเนินการ แต่จะเพิ่มความซับซ้อนและยังมีผลข้างเคียงบางอย่างเช่นการเพิ่มขึ้นของหน่วยความจำแบบสุ่ม
2) การดำเนินการพร้อมกันและการหยุดแอปพลิเคชัน: ตามชื่อแนะนำวิธีการรวบรวมขยะจะทำให้แอปพลิเคชันระงับในขณะที่ทำการรวบรวมขยะ แม้ว่าการรวบรวมขยะของการดำเนินการพร้อมกันจะไม่ทำให้แอปพลิเคชันหยุดชั่วคราวเนื่องจากการดำเนินการที่เกิดขึ้นพร้อมกันขยะจำเป็นต้องแก้ไขความขัดแย้งกับแอปพลิเคชัน (แอปพลิเคชันอาจปรับเปลี่ยนวัตถุในระหว่างกระบวนการรวบรวมขยะ) ระบบเหนือศีรษะของการดำเนินการสะสมขยะพร้อมกัน
3) การบีบอัดและไม่บีบอัดและคัดลอก:
①ตัวเก็บขยะที่รองรับการบีบอัด (mark-inpression = mark-clear + การบีบอัด) จะย้ายวัตถุที่เข้าถึงได้ทั้งหมดเข้าด้วยกันจากนั้นรีไซเคิลหน่วยความจำที่ถูกครอบครองทั้งหมดก่อนหน้านี้ลดการกระจายตัวของหน่วยความจำ
②ตัวเก็บขยะที่ไม่มีการบีบอัด (ทำเครื่องหมาย) จะต้องผ่านการสำรวจสองครั้ง ครั้งแรกที่คุณเข้าถึงวัตถุที่เข้าถึงได้ทั้งหมดและทำเครื่องหมายเป็นสถานะที่เข้าถึงได้ ครั้งที่สองที่คุณอำนวยความสะดวกในพื้นที่หน่วยความจำทั้งหมดและรีไซเคิลวัตถุที่ไม่ได้ทำเครื่องหมายสถานะที่เข้าถึงได้ วิธีการรีไซเคิลนี้ไม่ได้ถูกบีบอัดและไม่จำเป็นต้องมีหน่วยความจำเพิ่มเติม แต่จะทำให้เกิดการกระจายตัวหากต้องใช้การสำรวจสองครั้ง
③คัดลอก Garbage Collector: แบ่งหน่วยความจำฮีปออกเป็นสองช่องว่างที่เหมือนกันเข้าถึงวัตถุที่เข้าถึงได้แต่ละชิ้นจากรูท (คล้ายกับจุดสุดยอดเริ่มต้นของกราฟกำกับก่อนหน้านี้) คัดลอกวัตถุที่เข้าถึงได้ทั้งหมดในอวกาศ A ถึงอวกาศ B แล้วรีไซเคิลพื้นที่ A ในครั้งเดียว สำหรับอัลกอริทึมนี้เนื่องจากคุณต้องเข้าถึงวัตถุที่เข้าถึงได้ทั้งหมดคัดลอกวัตถุที่เข้าถึงได้ทั้งหมดออกไปและรีไซเคิลพื้นที่ทั้งหมดโดยตรงโดยไม่สนใจวัตถุที่ไม่สามารถเข้าถึงได้เลยค่าใช้จ่ายในการข้ามพื้นที่มีขนาดเล็ก แต่ต้องใช้ค่าใช้จ่ายในการคัดลอกจำนวนมากและหน่วยความจำมากขึ้น
3.2 การรีไซเคิลการสร้างหน่วยความจำกอง
1) พื้นฐานสำหรับการรีไซเคิล Generational:
①ความยาวของเวลาการอยู่รอดของวัตถุ: วัตถุส่วนใหญ่รีไซเคิลในช่วงเวลาหนุ่มสาว②รุ่นที่แตกต่างกันใช้กลยุทธ์การรีไซเคิลขยะที่แตกต่างกัน: ใหม่ (เวลารอดชีวิตสั้น ๆ ) เก่า (เวลารอดชีวิตนาน) ไม่ค่อยมีการอ้างอิงระหว่างวัตถุ
2) การสร้างหน่วยความจำกอง:
①รุ่นเยาว์:
ⅰกลไกการรีไซเคิล: เนื่องจากจำนวนวัตถุมีขนาดเล็กการจำลองแบบและการรีไซเคิลจึงถูกนำมาใช้
ⅱพื้นที่รวม: ประกอบด้วย 1 พื้นที่อีเดนและ 2 พื้นที่ผู้รอดชีวิต สองพื้นที่ผู้รอดชีวิตในเวลาเดียวกันหนึ่งถูกใช้เพื่อบันทึกวัตถุและอีกพื้นที่ว่างเปล่า ทุกครั้งที่มีการดำเนินการเก็บขยะรุ่นเยาวชนวัตถุที่เข้าถึงได้ในอีเด็นและจากการคัดลอกไปยังพื้นที่และบางส่วนของชีวิตที่ยาวนานจะถูกคัดลอกไปยังวัยชราจากนั้นอีเด็นและอวกาศจะถูกล้างออกและในที่สุด
ⅲแหล่งที่มาของวัตถุ: วัตถุส่วนใหญ่ถูกกำหนดให้กับพื้นที่อีเด็นก่อนและวัตถุขนาดใหญ่บางชิ้นจะถูกกำหนดโดยตรงให้กับรุ่นเก่า
ⅳความถี่ในการรีไซเคิล: เนื่องจากวัตถุรุ่นเยาวชนส่วนใหญ่เข้าสู่สถานะที่ไม่สามารถเข้าถึงได้อย่างรวดเร็วความถี่ในการรีไซเคิลจึงสูงและความเร็วในการรีไซเคิลนั้นเร็ว
รุ่น②lold:
ⅰกลไกการรีไซเคิล: ใช้อัลกอริทึมการบีบอัดของเครื่องหมายเพื่อกู้คืน
ⅱแหล่งที่มาของวัตถุ: 1. วัตถุขนาดใหญ่เข้าสู่วัยชราโดยตรง
2. ความถี่ในการรีไซเคิลของวัตถุที่เข้าถึงได้ด้วยเวลาการอยู่รอดที่ยาวนานในรุ่นเยาวชน: เนื่องจากวัตถุไม่กี่ชิ้นตายความถี่การดำเนินการไม่สูงและใช้เวลานานกว่าจะเสร็จสมบูรณ์
③permanent Generation:
ⅰpurpose: ใช้ในการโหลดคลาสวิธีการและข้อมูลอื่น ๆ ค่าเริ่มต้นคือ 64m และจะไม่รีไซเคิล ⅱObjectที่มา: เช่น: สำหรับเฟรมเวิร์กเช่นไฮเบอร์เนตและสปริงที่เหมือนกับคลาสการสร้างแบบไดนามิก AOP มักจะสร้างคลาสพร็อกซีแบบไดนามิกจำนวนมากดังนั้นจึงจำเป็นต้องมีหน่วยความจำการสร้างถาวรมากขึ้น ดังนั้นเรามักจะพบ java.lang.outofMemoryError: ข้อผิดพลาดในพื้นที่ Permgen เมื่อทำการดีบักไฮเบอร์เนต นี่คือข้อผิดพลาดที่เกิดจากการอ่อนเพลียของหน่วยความจำถาวร
ⅲความถี่ในการรีไซเคิล: จะไม่รีไซเคิล
3.3 นักสะสมขยะทั่วไป
1) การรีไซเคิลอนุกรม (ใช้ CPU เพียงครั้งเดียว): รุ่นใหม่ใช้อัลกอริทึมการคัดลอกอนุกรม; รุ่นเก่าใช้อัลกอริทึมการบีบอัดเครื่องหมายอนุกรม (สามขั้นตอน: Mark Mark - Clear Sweep - Compress Compact) โปรแกรมจะถูกหยุดชั่วคราวในช่วงระยะเวลาการรีไซเคิล
2) Recycler แบบขนาน: อัลกอริทึมที่ใช้สำหรับคนรุ่นใหม่นั้นเหมือนกับรีไซเคิลแบบอนุกรม แต่จะเพิ่มการประมวลผลแบบ Multi-CPU แบบขนานเท่านั้น การประมวลผลของคนรุ่นเก่านั้นเหมือนกับของรีไซเคิลอนุกรมและยังคงเป็นเธรดเดียว
3) ตัวสะสมการบีบอัดแบบขนาน: การประมวลผลของคนรุ่นใหม่นั้นเป็นอัลกอริทึมเดียวกับของตัวสะสมแบบขนาน แต่อัลกอริทึมที่แตกต่างกันใช้สำหรับคนรุ่นเก่าซึ่งแบ่งออกเป็นภูมิภาคต่าง ๆ แล้วการติดฉลากและอัลกอริทึมการบีบอัด:
①แบ่งเก่าเป็นหลายพื้นที่คงที่;
② Mark Stage (แบบมัลติเธรดขนาน), การทำเครื่องหมายวัตถุที่เข้าถึงได้;
③ขั้นตอนสรุป (การดำเนินการแบบอนุกรม) เมื่อคุณพบพื้นที่ที่มีค่าตัวเลข (ความหนาแน่นของวัตถุต่ำ) จากด้านซ้ายพื้นที่นี้และพื้นที่ด้านขวาจะถูกบีบอัดและกู้คืน ปลายด้านซ้ายคือพื้นที่หนาแน่น④ขั้นตอนกะทัดรัด (ขนานแบบมัลติเธรด) ระบุพื้นที่ที่ต้องโหลดและคัดลอกข้อมูลลงในพื้นที่เหล่านี้ในแบบคู่ขนาน หลังจากกระบวนการนี้มีวัตถุที่ใช้งานอยู่จำนวนมากที่ปลายด้านหนึ่งของรุ่นเก่าและพื้นที่ขนาดใหญ่ที่ปลายอีกด้าน
4) การระบุตัวตนพร้อมกัน - การทำความสะอาดและรีไซเคิล (CMS): การประมวลผลของคนรุ่นใหม่นั้นเป็นอัลกอริทึมเดียวกับของรีไซเคิลแบบขนาน แต่อัลกอริทึมที่แตกต่างกันใช้สำหรับรุ่นเก่า แต่ยังคงใช้อัลกอริทึมการทำความสะอาดเครื่องหมายอยู่:
①การระบุเริ่มต้น (โปรแกรมหยุดชั่วคราว): ทำเครื่องหมายวัตถุที่อ้างอิงโดยตรง (วัตถุระดับแรก);
ide การระบุตัวตนที่เกิดขึ้นพร้อมกัน (การรันโปรแกรม): ค้นหาวัตถุที่เข้าถึงได้อื่น ๆ ผ่านวัตถุระดับแรก
③ re-mark (โปรแกรมหยุดชั่วคราว): วัตถุทำเครื่องหมายซ้ำแบบหลายเธรดคู่ขนานที่อาจพลาดไปเนื่องจากการเกิดขึ้นพร้อมกัน (เพียงแค่พูดมันเป็นการต่อต้านการพลาด)
④การทำความสะอาดพร้อมกัน (โปรแกรมทำงาน)
4. เคล็ดลับการจัดการหน่วยความจำ
1) พยายามใช้ปริมาณโดยตรงเช่น: String javastr = "กระบวนการเติบโตของการฝึกงานในโรงเรียนประถมศึกษา";
2) ใช้ StringBuilder และ StringBuffer เพื่อดำเนินการต่อการต่อการเชื่อมต่อสตริงและการดำเนินการอื่น ๆ
3) ปล่อยวัตถุที่ไร้ประโยชน์โดยเร็วที่สุด
4) พยายามใช้ตัวแปรคงที่ให้น้อยที่สุด
5) แคชวัตถุที่ใช้กันทั่วไป: สามารถนำไปใช้งานได้โดยใช้แคชโอเพนซอร์สโอเพ่นซอร์สเช่น: ออสกาเช่, ehcache;
6) พยายามอย่าใช้วิธีการสรุป ();
7) คุณสามารถพิจารณาใช้ softreference อ้างอิงอ่อนเมื่อจำเป็น
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น