เมื่อโปรแกรมสร้างเอนทิตีประเภทอ้างอิงเช่นวัตถุอาร์เรย์ ฯลฯ ระบบจะจัดสรรชิ้นส่วนของหน่วยความจำสำหรับวัตถุในหน่วยความจำฮีปและวัตถุจะถูกเก็บไว้ในหน่วยความจำชิ้นนี้ เมื่อหน่วยความจำชิ้นนี้ไม่ได้อ้างอิงโดยตัวแปรอ้างอิงใด ๆ อีกต่อไปชิ้นส่วนของหน่วยความจำจะกลายเป็นขยะรอกลไกการรวบรวมขยะที่จะรีไซเคิล กลไกการรวบรวมขยะมีสามลักษณะ:
กลไกการรวบรวมขยะมีความรับผิดชอบเฉพาะในการรีไซเคิลวัตถุในหน่วยความจำฮีปและจะไม่รีไซเคิลทรัพยากรทางกายภาพใด ๆ (เช่นการเชื่อมต่อฐานข้อมูลทรัพยากรเปิดไฟล์ ฯลฯ ) และจะไม่รีไซเคิลหน่วยความจำที่จัดสรรให้กับวัตถุในวิธีอื่นนอกเหนือจากวิธีการสร้างวัตถุ (เช่นหน่วยความจำที่ใช้โดยวัตถุ
โปรแกรมไม่สามารถควบคุมการทำงานของการรวบรวมขยะได้อย่างถูกต้องดังนั้นจึงสามารถแนะนำให้ใช้ในการรวบรวมขยะเท่านั้น มีสองวิธีที่แนะนำ: system.gc () และ runtime.getruntime (). gc ()
ก่อนที่จะรวบรวมขยะวิธีการสรุป () จะถูกเรียกก่อนเสมอ แต่มันก็เหมือนกับเวลารวบรวมขยะและวิธีการสรุป () ก็ไม่แน่ใจเช่นกัน
เกี่ยวกับสามลักษณะข้างต้นมีสามปัญหา:
1. งานทำความสะอาดจะต้องทำด้วยตนเองเพื่อเพิ่มหน่วยความจำและทรัพยากรทางกายภาพอื่น ๆ ที่จัดสรรในลักษณะอื่นนอกเหนือจากวิธีการสร้างวัตถุ และระมัดระวังในการกำจัดการอ้างอิงวัตถุที่หมดอายุไม่เช่นนั้นอาจเกิดจาก oom
การทำความสะอาดด้วยตนเองมักจะใช้โครงสร้างรหัสเช่นลอง ... ในที่สุด ...
ตัวอย่างมีดังนี้:
นำเข้า java.io.fileinputstream; นำเข้า java.io.filenotfoundexception; นำเข้า java.io.ioexception; คู่มือระดับสาธารณะ {โมฆะคงที่สาธารณะ (สตริง [] args) {fileInputStream fileInputStream = null; ลอง {fileInputStream = new FileInputStream ("./ src/manualClear.java"); } catch (filenotfoundexception e) {system.out.println (e.getMessage ()); E.PrintStackTrace (); กลับ; } ลอง {byte [] bbuf = byte ใหม่ [1024]; int hasread = 0; ลอง {ในขณะที่ ((hasread = fileInputStream.read (bbuf))> 0) {system.out.println (สตริงใหม่ (bbuf, 0, hasread)); }} catch (ioexception e) {e.printstacktrace (); }} ในที่สุด {ลอง {fileInputStream.close (); } catch (ioexception e) {e.printstacktrace (); -โดยปกติจะมีสามกรณี OOM ทั่วไปที่เกิดจากการอ้างอิงถึงวัตถุที่หมดอายุ ทั้งสามกรณีเหล่านี้มักจะไม่ง่ายที่จะตรวจจับและจะไม่มีปัญหาในการทำงานในช่วงเวลาสั้น ๆ อย่างไรก็ตามหลังจากผ่านไปนานจำนวนวัตถุที่รั่วไหลออกมาจะทำให้โปรแกรมผิดพลาดในที่สุด
เมื่อชั้นเรียนจัดการหน่วยความจำด้วยตัวเองคุณควรระวังการรั่วไหลของหน่วยความจำดังนี้:
นำเข้า java.util.Arrays; นำเข้า java.util.empystackexception; สแต็คคลาส {วัตถุส่วนตัว [] องค์ประกอบ; ขนาด int ส่วนตัว; INT สุดท้ายคงที่ int default_inital_capacity = 16; สแต็กสาธารณะ () {องค์ประกอบ = วัตถุใหม่ [default_inital_capacity]; } โมฆะสาธารณะผลัก (วัตถุ E) {ensureCapacity (); องค์ประกอบ [ขนาด ++] = e; } วัตถุสาธารณะป๊อป () {ถ้า (ขนาด == 0) {โยน emptystackexception ใหม่ (); } return elements [-size]; } โมฆะส่วนตัว ensureCapacity () {if (elements.length == ขนาด) {องค์ประกอบ = array.copyof (องค์ประกอบ 2 * ขนาด + 1); }}} คลาสสาธารณะ stackDemo {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สแต็กสแต็ก = ใหม่สแต็ก (); สำหรับ (int i = 0; i <10,000; i ++) {stack.push (วัตถุใหม่ ()); } สำหรับ (int i = 0; i <10000; i ++) {stack.pop (); -เหตุผลสำหรับการรั่วไหลของหน่วยความจำคือแม้ว่าวัตถุอื่น ๆ ในสแต็กจะไม่ถูกอ้างอิงอีกต่อไปองค์ประกอบ [] อาร์เรย์ในคลาสสแต็กยังคงอ้างอิงถึงวัตถุเหล่านี้ทำให้วัตถุไม่ถูกนำกลับมาใช้ใหม่โดยการรวบรวมขยะ ดังนั้นเมื่อชั้นเรียนจำเป็นต้องจัดการหน่วยความจำด้วยตัวเองระวังว่าการอ้างอิงที่หมดอายุเหล่านี้ได้รับการดูแลโดยภายในนั้นจะถูกเรียกใช้งานในเวลาหรือไม่ ในตัวอย่างนี้หลังจากสแต็กถูกปล่อยออกมาแล้วจะแสดงผล
องค์ประกอบ [ขนาด] = null;
แคชจะต้องระวังการรั่วไหลของหน่วยความจำ สถานการณ์นี้มักจะเป็นกรณีที่เมื่อวัตถุถูกใส่เข้าไปในแคชก็เป็นไปได้ว่ามันจะง่ายที่จะลืมถ้ามันไม่ได้ใช้เป็นเวลานาน โดยปกติแล้ว WakeHashMap สามารถใช้เป็นตัวแทนของแคช หลังจากรายการในแคชหมดอายุพวกเขาสามารถลบได้โดยอัตโนมัติ หรือสามารถดำเนินการเป็นระยะโดยเธรดพื้นหลังเพื่อล้างรายการที่หมดอายุในบัฟเฟอร์
การลงทะเบียนผู้ฟังหรือการโทรกลับจะแสดงที่ดีที่สุดเพื่อยกเลิกการลงทะเบียน
2. อย่าเรียก finalize () ด้วยตนเองมันถูกเรียกไปที่นักสะสมขยะ
3. หลีกเลี่ยงการใช้วิธีการสรุป () เว้นแต่จะใช้เป็นการตัดสินเงื่อนไขสิ้นสุดเพื่อค้นหาว่าวัตถุนั้นยังไม่ได้รับการทำความสะอาดอย่างเหมาะสม มันถูกใช้เป็นเครือข่ายความปลอดภัยในการทำความสะอาดทรัพยากรระบบเมื่อทำความสะอาดด้วยตนเองลืมโทร ไม่ควรทำความสะอาดล่าช้า หากคุณบันทึกข้อมูลเกี่ยวกับทรัพยากรการทำความสะอาดที่ถูกลืมในเวลาเดียวกันมันก็สะดวกสำหรับข้อผิดพลาดที่จะค้นพบในภายหลังและแก้ไขรหัสการทำความสะอาดที่ถูกลืมในเวลา เพิ่มทรัพยากรระบบวิกฤตที่ไม่ได้รับจากวิธีการท้องถิ่นในวัตถุ
เนื่องจากวิธีการสรุป () ไม่ได้รับการรับรองอย่างถูกต้องจึงเป็นการดีที่สุดที่จะไม่ปล่อยทรัพยากรที่สำคัญ แต่สามารถใช้ในสามกรณีที่กล่าวถึงข้างต้น กรณีแรกมีดังนี้:
หนังสือชั้นเรียน {boolean checkout = false; หนังสือสาธารณะ (เช็คเอาต์บูลีน) {this.checkout = เช็คเอาต์; } public void checkin () {checkout = false; } @Override void protected finalize () โยน throwable {if (checkout) {system.out.println ("ข้อผิดพลาด: ตรวจสอบ"); }}} คลาสสาธารณะ FinalizeCheckObjectuse {โมฆะคงที่สาธารณะหลัก (String [] args) {หนังสือเล่มใหม่ (จริง); System.gc (); -ผลการดำเนินการ:
ข้อผิดพลาด: ตรวจสอบ
วัตถุหนังสือในตัวอย่างจะต้องอยู่ในสถานะ Checkin ก่อนที่จะถูกปล่อยออกมามิฉะนั้นจะไม่สามารถปล่อยออกมาได้ การใช้งานใน Finalize สามารถช่วยค้นพบวัตถุที่ผิดกฎหมายในเวลาหรือมากกว่าโดยตรงใช้ตัวแปรอ้างอิงเพื่ออ้างถึงโดยตรงในการสรุปเพื่อให้สามารถเข้าสู่สถานะของการเข้าถึงได้อีกครั้งแล้วประมวลผลอีกครั้ง
อีกประเด็นหนึ่งที่ควรทราบคือหากคลาสย่อยแทนที่วิธีการสรุปของคลาสแม่ แต่ลืมที่จะเรียกด้วยตนเอง super.finolize หรือกระบวนการสุดท้ายของคลาสย่อยมีข้อยกเว้นส่งผลให้ super.finolize ไม่ถูกดำเนินการแล้ววิธีการสิ้นสุดของคลาสแม่จะไม่ถูกเรียก
ดังนี้:
ชั้นเรียนพาเรนต์ {@Override void protected finalize () โยน throwable {system.out.println (getClass (). getName () + "จบการเริ่มต้น"); }} Class SON ขยายพาเรนต์ {@Override void protected finalize () โยน throwable {system.out.println (getClass (). getName () + "จบการเริ่มต้น"); }} คลาสสาธารณะ superfinalizelost {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ลูกชายคนใหม่ (); System.gc (); -ผลการทำงาน:
ลูกชายจบการเริ่มต้น
หรือ
ชั้นเรียนพาเรนต์ {@Override void protected finalize () โยน throwable {system.out.println (getClass (). getName () + "จบการเริ่มต้น"); }} Class SON ขยายพาเรนต์ {@Override void protected finalize () โยน throwable {system.out.println (getClass (). getName () + "จบการเริ่มต้น"); int i = 5 /0; super.finolize (); }} คลาสสาธารณะ superfinalizelost {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ลูกชายคนใหม่ (); System.gc (); -ผลการดำเนินการ:
ลูกชายจบการเริ่มต้น
สำหรับกรณีที่สองคุณสามารถใช้การลอง ... ในที่สุด ... โครงสร้างเพื่อแก้ปัญหา แต่สำหรับกรณีแรกมันจะเป็นการดีกว่าที่จะใช้วิธีที่เรียกว่าวิธีการสิ้นสุดของผู้พิทักษ์ ตัวอย่างมีดังนี้
Class Parent2 {วัตถุสุดท้ายส่วนตัว finalizeGuardian = new Object () {void protected finalize () โยน {system.out.println ("ดำเนินการตรรกะในวิธีการยกเลิกคลาสหลักที่นี่"); - };} class son2 ขยาย parent2 {@Override void protected finalize () โยน throwable {system.out.println (getClass (). getName () + "สุดท้ายเริ่มต้น"); int i = 5 /0; super.finolize (); }} คลาสสาธารณะ finalizeGuardian {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {son2 ใหม่ (); System.gc (); -ผลการดำเนินการ:
ดำเนินการตรรกะในวิธีการเลิกเรียนคลาสแม่ที่นี่
SON2 จบการเริ่มต้น
สิ่งนี้ทำให้มั่นใจได้ว่าการดำเนินการที่จำเป็นในวิธีการสิ้นสุดของคลาสแม่จะถูกดำเนินการ
ข้างต้นเป็นเรื่องเกี่ยวกับบทความนี้ฉันหวังว่ามันจะเป็นประโยชน์กับการเรียนรู้ของทุกคน