มีสี่ประเภทอ้างอิงใน Java/Android คือ:
การอ้างอิงที่แข็งแกร่ง - การอ้างอิงที่แข็งแกร่ง
ข้อมูลอ้างอิงที่อ่อนนุ่ม - ข้อมูลอ้างอิงที่อ่อนนุ่ม
การอ้างอิงที่อ่อนแอ - การอ้างอิงที่อ่อนแอ
การอ้างอิง Phantom - ใบเสนอราคาเสมือนจริง
ประเภทอ้างอิงที่แตกต่างกันมีลักษณะที่แตกต่างกันและสอดคล้องกับสถานการณ์การใช้งานที่แตกต่างกัน
1. การอ้างอิงที่ชัดเจน - การอ้างอิงที่แข็งแกร่ง
ประเภทการอ้างอิงที่พบบ่อยที่สุดในการเข้ารหัสจริง รูปแบบทั่วไปเช่น: a = new a (); ฯลฯ การอ้างอิงที่แข็งแกร่งจะถูกเก็บไว้ในหน่วยความจำสแต็กและเก็บที่อยู่ไว้ในวัตถุในหน่วยความจำ โดยทั่วไปเมื่อไม่มีการอ้างอิงที่แข็งแกร่งใด ๆ กับวัตถุในหน่วยความจำที่ชี้ไปที่มันเครื่องรวบรวมขยะเริ่มพิจารณาคอลเลกชันขยะที่อาจทำในหน่วยความจำนี้ หากการเข้ารหัส: a = null ในเวลานี้ที่อยู่ที่จัดสรรในกองและสร้างไม่มีการอ้างอิงอื่น ๆ เมื่อระบบดำเนินการรวบรวมขยะหน่วยความจำกองจะถูกเก็บรวบรวมขยะ
Softreference, Weakreference และ Phantomreference เป็นคลาสย่อยทั้งหมดของคลาส java.lang.ref.reference การอ้างอิงเป็นคลาสพื้นฐานที่เป็นนามธรรมกำหนดการดำเนินการพื้นฐานของวัตถุคลาสย่อย คลาสย่อยอ้างอิงทั้งหมดมีคุณสมบัติดังต่อไปนี้:
1. ไม่สามารถสร้างคลาสย่อยอ้างอิงได้โดยตรงโดยไม่ต้องพารามิเตอร์ อย่างน้อยก็ต้องใช้วัตถุอ้างอิงที่แข็งแกร่งเป็นพารามิเตอร์การก่อสร้างเพื่อสร้างวัตถุคลาสย่อยที่เกี่ยวข้อง
2. เนื่องจากวัตถุถูกสร้างขึ้นใน 1 ด้วยวัตถุอ้างอิงที่แข็งแกร่งเป็นพารามิเตอร์การก่อสร้างวัตถุในหน่วยความจำฮีปที่ชี้ไปที่การอ้างอิงที่แข็งแกร่ง แต่เดิมจะไม่เกี่ยวข้องโดยตรงกับการอ้างอิงที่แข็งแกร่งอีกต่อไป แต่จะมีการเชื่อมต่อบางอย่างกับการอ้างอิงของวัตถุย่อยของการอ้างอิง และการเชื่อมต่อนี้อาจส่งผลต่อการรวบรวมขยะของวัตถุ
ตามลักษณะอิทธิพลที่แตกต่างกันของวัตถุคลาสย่อยที่แตกต่างกันในการรวบรวมขยะของวัตถุตัวบ่งชี้ (การอ้างอิงที่แข็งแกร่งไปยังวัตถุในหน่วยความจำฮีปชี้ไปที่) สามคลาสย่อยจะเกิดขึ้นคือ softreference, deakreference และ phantomreference
2. การอ้างอิงแบบซอฟท์ - ข้อมูลอ้างอิงที่อ่อนนุ่ม
รูปแบบการใช้งานทั่วไปของการอ้างอิงที่อ่อนนุ่มมีดังนี้:
a = ใหม่ a ();
Softreference <a> SRA = ใหม่ softreference <a> (a);
ผ่านการอ้างอิงที่แข็งแกร่งของวัตถุเป็นพารามิเตอร์วัตถุ softreference ถูกสร้างขึ้นและ WRA ในหน่วยความจำสแต็กชี้ไปที่วัตถุนี้
ในเวลานี้การเข้ารหัสต่อไปนี้จะดำเนินการ: a = null มันมีผลกระทบอะไรต่อการรวบรวมขยะของวัตถุที่ชี้ไปที่ A?
มาดูผลลัพธ์ผลลัพธ์ของโปรแกรมต่อไปนี้:
นำเข้า java.lang.ref.softreference; การอ้างอิงคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {a a = new a (); Softreference <a> SRA = ใหม่ softreference <a> (a); a = null; if (sra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + sra.get ()); } // Garbage Collection System.gc (); if (sra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + sra.get ()); }}} คลาส A {} ## ผลลัพธ์ผลลัพธ์คือ:
1 วัตถุยังไม่ได้รีไซเคิล@4807CCF62 วัตถุไม่ได้ถูกนำกลับมาใช้ใหม่@4807CCF6
เมื่อ a = null วัตถุในหน่วยความจำฮีปจะไม่มีการอ้างอิงที่แข็งแกร่งอีกต่อไป แต่มีวัตถุแฟชั่นที่อ้างอิงโดย SRA ชี้ไปที่วัตถุ เมื่อวิธี SRA.Get () ถูกเรียกเป็นครั้งแรกในการส่งคืนวัตถุตัวบ่งชี้นี้เนื่องจากตัวเก็บขยะมีแนวโน้มที่จะยังไม่ได้ทำการรวบรวมขยะ Get () มีผลในเวลานี้ซึ่งง่ายต่อการเข้าใจ เมื่อโปรแกรมดำเนินการ System.gc (); การบังคับให้เก็บขยะผ่าน SRA.Get () พบว่าวัตถุที่ระบุยังสามารถรับได้ซึ่งบ่งชี้ว่าวัตถุ A ไม่ได้ถูกเก็บรวบรวมขยะ ดังนั้นวัตถุที่ระบุโดยการอ้างอิงที่อ่อนนุ่มจะเริ่มเก็บขยะเมื่อใด ต้องปฏิบัติตามสองเงื่อนไขต่อไปนี้:
1. เมื่อวัตถุที่ระบุว่าไม่มีวัตถุอ้างอิงที่แข็งแกร่งใด ๆ ชี้ไปที่มัน;
2. เมื่อเครื่องเสมือนมีหน่วยความจำไม่เพียงพอ
ดังนั้น Softreference จะขยายเวลาที่ระบุว่าวัตถุนั้นใช้หน่วยความจำฮีปจนกระทั่งเครื่องเสมือนมีหน่วยความจำไม่เพียงพอ ตัวเก็บขยะไม่ได้รีไซเคิลพื้นที่หน่วยความจำกองนี้
3. การอ้างอิงที่มีการอ้างอิงที่อ่อนแอ
ในทำนองเดียวกันรูปแบบการใช้งานทั่วไปของการอ้างอิงที่อ่อนนุ่มมีดังนี้:
a = ใหม่ a ();
อ่อนแอ <a> wra = new beakreference <a> (a);
เมื่อไม่มีการอ้างอิงที่แข็งแกร่งชี้ไปที่วัตถุนี้ลักษณะการรวบรวมขยะคืออะไร?
นำเข้า java.lang.ref.weakreference; การอ้างอิงคลาสสาธารณะ {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {a a = new a (); อ่อนแอ <a> wra = new beakreference <a> (a); a = null; if (wra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + wra.get ()); } // Garbage Collection System.gc (); if (wra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + wra.get ()); }}} คลาส A {} ## ผลลัพธ์ผลลัพธ์คือ:
วัตถุยังไม่ได้รีไซเคิล แต่วัตถุ@52E5376AA เข้าสู่กระบวนการรวบรวมขยะ
ผลลัพธ์แรกที่ได้รับการอธิบายดังกล่าวข้างต้น หลังจากการรวบรวมขยะ WRA.GET () จะส่งคืนค่า NULL ซึ่งบ่งชี้ว่ามันบ่งชี้ว่าวัตถุได้เข้าสู่กระบวนการรวบรวมขยะ ดังนั้นลักษณะของการอ้างอิงที่อ่อนแอจึงสรุปเป็น:
Weakreference ไม่ได้เปลี่ยนระยะเวลาการรวบรวมขยะของวัตถุอ้างอิงที่แข็งแกร่งดั้งเดิม เมื่อมันระบุว่าวัตถุไม่มีวัตถุอ้างอิงที่แข็งแกร่งวัตถุจะเข้าสู่กระบวนการรวบรวมขยะปกติ
ดังนั้นตามลักษณะนี้มีคำถาม: ความสำคัญของการอ่อนแอคืออะไร?
สถานการณ์การใช้งานหลักคือ: ขณะนี้มีการอ้างอิงที่แข็งแกร่งที่ชี้ไปที่วัตถุอ้างอิงที่แข็งแกร่ง ในเวลานี้เนื่องจากความต้องการทางธุรกิจจำเป็นต้องเพิ่มการอ้างอิงไปยังวัตถุนี้และในเวลาเดียวกันก็ไม่ได้มีวัตถุประสงค์เพื่อเปลี่ยนระยะเวลาการรวบรวมขยะของการอ้างอิงนี้ ในเวลานี้ Weakreference เพิ่งตอบสนองความต้องการและพบได้ทั่วไปในบางสถานการณ์ที่มีวงจรชีวิต
ต่อไปนี้เป็นสถานการณ์จำลองสำหรับการใช้งานที่อ่อนแอใน Android - การรวมคลาสภายในแบบคงที่และการอ้างอิงที่อ่อนแอเพื่อแก้ปัญหาการรั่วไหลของหน่วยความจำที่เป็นไปได้ในกิจกรรม
ในกิจกรรมเราต้องสร้างเธรดใหม่เพื่อรับข้อมูลและใช้วิธีการจัดการ - SendMessage นี่คือรหัสทั่วไปสำหรับกระบวนการนี้:
MainActivity ระดับสาธารณะขยายกิจกรรม {// ... หน้า int ส่วนตัว; Handler ส่วนตัว = handler ใหม่ () {@Override โมฆะสาธารณะ handleMessage (ข้อความข้อความ) {ถ้า (msg.what == 1) {// ... หน้า ++; } อื่น { //... } }; - @Override ป้องกันโมฆะ onCreate (Bundle SavedInstancestate) {super.oncreate (savedinstancestate); SetContentView (R.Layout.Activity_Main); // ... เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะเรียกใช้ () {// .. ข้อความ msg = message.obtain (); msg.what = 1; //msg.obj = xx; handler.sendMessage (msg);}}). เริ่มต้น (); -Run Link ใน Eclispe คุณจะเห็นข้อความเตือน: คลาส Handler นี้ควรเป็นแบบคงที่หรืออาจเกิดขึ้นได้ ... คลิกเพื่อดูข้อมูลนี้ซึ่งอธิบายปัญหาในรายละเอียดและให้ทางออกที่มีการชี้นำ
ปัญหา: ตรวจสอบให้แน่ใจว่าคลาส Handler ไม่ได้ยึดถือการอ้างอิงถึงชั้นนอกชั้นนอก: HandlerLeaks -Inten Handler นี้ถูกประกาศว่าเป็นชั้นเรียนภายในมันอาจป้องกันไม่ให้ชั้นนอกถูกเก็บรวบรวม หากตัวจัดการใช้ลูปหรือข้อความสำหรับเธรดนอกเหนือจากเธรดหลักแล้วจะไม่มีปัญหา หากตัวจัดการใช้ Looper หรือ MessageQueue ของเธรดหลักคุณจะต้องแก้ไขการประกาศตัวจัดการของคุณดังต่อไปนี้: ประกาศตัวจัดการเป็นคลาสคงที่; ในชั้นนอกชั้นนอกจะสร้างความอ่อนแอให้กับชั้นนอกและส่งวัตถุนี้ไปยังตัวจัดการของคุณ ทำการอ้างอิงทั้งหมดไปยังสมาชิกของชั้นนอกโดยใช้วัตถุ Weakreference
ความหมายทั่วไปคือขอแนะนำให้กำหนดตัวจัดการเป็นคลาสคงที่ภายในและกำหนดการอ้างอิงถึงความอ่อนแอในระดับภายในแบบคงที่นี้เนื่องจากการระบุวัตถุกิจกรรมภายนอก
การวิเคราะห์ปัญหา:
กิจกรรมมีวงจรชีวิตของตัวเอง ในระหว่างกระบวนการทำงานของเธรดที่เปิดใหม่ในกิจกรรมผู้ใช้อาจกดปุ่มย้อนกลับหรือระบบไม่เพียงพอหน่วยความจำ ฯลฯ เพื่อรีไซเคิลกิจกรรมนี้ เนื่องจากเธรดที่เพิ่งเปิดตัวใหม่ในกิจกรรมจะไม่เป็นไปตามวัฏจักรของกิจกรรมนั่นคือเมื่อกิจกรรมดำเนินการ ondestroy เนื่องจากการมีอยู่ของเธรดและ handlemessage ของ Handler ระบบ แต่เดิมหวังว่าจะทำการกู้คืนหน่วยความจำของกิจกรรมนี้
ดังนั้นเมื่อใช้ตัวจัดการในกิจกรรมในมือข้างหนึ่งมันจะต้องถูกกำหนดให้เป็นรูปแบบคลาสภายในแบบคงที่เพื่อให้สามารถแยกออกจากคลาสภายนอกและไม่มีการอ้างอิงไปยังคลาสภายนอกอีกต่อไป ในเวลาเดียวกันเนื่องจากพกพาในตัวจัดการโดยทั่วไปจำเป็นต้องเข้าถึงหรือปรับเปลี่ยนคุณสมบัติของกิจกรรมในเวลานี้ผู้อ่อนแอที่ชี้ไปที่กิจกรรมนี้จำเป็นต้องมีการกำหนดภายในตัวจัดการเพื่อที่จะไม่ส่งผลกระทบต่อการกู้คืนหน่วยความจำของกิจกรรม ในเวลาเดียวกันคุณสมบัติของกิจกรรมสามารถเข้าถึงได้ภายใต้สถานการณ์ปกติ
คำแนะนำของ Google อย่างเป็นทางการคือ:
MainActivity ระดับสาธารณะขยายกิจกรรม {// ... หน้า int ส่วนตัว; ส่วนตัว myhandler mmyhandler = ใหม่ myhandler (นี่); คลาสคงที่ระดับส่วนตัว MyHandler ขยายตัวจัดการ {Private Weakreference <Ainactivity> wractivity; สาธารณะ myhandler (กิจกรรมหลัก) {this.wractivity = new beakreference <mainactivity> (กิจกรรม); } @Override โมฆะสาธารณะ handleMessage (ข้อความข้อความ) {ถ้า (wractivity.get () == null) {return; } mainactivity mactivity = roctivity.get (); if (msg.what == 1) {// ... mactivity.page ++; } else {// ... }}} @Override Void onCreate (Bundle SavedInstancEstate) {super.onCreate (SavedInstancestate); SetContentView (R.Layout.Activity_Main); // ... เธรดใหม่ (ใหม่ runnable () {@Override public void run () {// .. ข้อความ msg = message.obtain (); msg.what = 1; //msg.obj = xx; mmyhandler.sendMessage (msg);}}) -สำหรับ softreference และ beakreference นอกจากนี้ยังมีพารามิเตอร์ตัวสร้าง reference -referentQueue <t> และเมื่อวัตถุที่ระบุโดย softreference หรือ hemfreference เป็นขยะที่เก็บรวบรวมจริงการอ้างอิงของมันจะถูกวางไว้ในอ้างอิง โปรดทราบว่าข้างต้นเมื่อวิธีการ Get () ของ softreference หรือ beakreference ส่งคืนค่า null จะระบุว่าวัตถุที่ระบุได้เข้าสู่กระบวนการรวบรวมขยะและวัตถุอาจไม่ได้เก็บขยะในเวลานี้ หลังจากยืนยันว่ามันได้รับการเก็บรวบรวมขยะถ้า referenceQueue เป็นข้อมูลอ้างอิงของมันจะถูกวางไว้ใน referenceQueue
ดูตัวอย่างด้านล่าง:
การอ้างอิงคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {a = new a (); อ่อนแอ <a> wra = new beakreference <a> (a); a = null; if (wra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + wra.get ()); } // Garbage Collection System.gc (); if (wra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + wra.get ()); }}} คลาส A {@Override void protected finalize () โยน throwable {super.finalize (); System.out.println ("อยู่ในขั้นสุดท้าย"); - ## ผลลัพธ์ผลลัพธ์คือ:
1 วัตถุยังไม่ได้รีไซเคิล@46993AAA2 วัตถุได้รับการรีไซเคิล 3 ในขั้นสุดท้าย
นอกจากนี้ยังตรวจสอบคำสั่ง "การป้อนกระบวนการเก็บขยะ" ที่กล่าวถึงข้างต้น ลองดูที่รหัสชิ้นหนึ่งร่วมกับ ReferenceQueue:
การอ้างอิงคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {a = new a (); ReferenceQueue <a> rq = new ReferenceQueue <a> (); อ่อนแอ <a> wra = new beakreference <a> (a, rq); a = null; if (wra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + wra.get ()); } system.out.println ("รายการ RQ:" + rq.poll ()); // Garbage Collection System.gc (); if (wra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + wra.get ()); } /* ลอง {thread.sleep (1,000); } catch (interruptedException e) {e.printStackTrace (); } */ system.out.println ("รายการ RQ:" + rq.poll ()); }} class A {@Override protected void finalize () โยน throwable {super.finalize (); System.out.println ("อยู่ในขั้นสุดท้าย"); - ## ผลลัพธ์ผลลัพธ์คือ:
วัตถุยังไม่ได้รีไซเคิล แต่รายการ@302B2C81RQ: วัตถุ NULLA เข้าสู่กระบวนการรวบรวมขยะรายการ RQ รายการ: nullin a finalize
ดังนั้นจึงมีการตรวจสอบว่า "การอ้างอิง softreference หรือการอ้างอิงที่อ่อนแอซึ่งป้อนเฉพาะกระบวนการรวบรวมขยะยังไม่ได้ถูกเพิ่มเข้าไปใน referenceQueue"
การอ้างอิงคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {a = new a (); ReferenceQueue <a> rq = new ReferenceQueue <a> (); อ่อนแอ <a> wra = new beakreference <a> (a, rq); a = null; if (wra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + wra.get ()); } system.out.println ("รายการ RQ:" + rq.poll ()); // Garbage Collection System.gc (); if (wra.get () == null) {system.out.println ("วัตถุเข้าสู่กระบวนการรวบรวมขยะ"); } else {system.out.println ("วัตถุยังไม่ได้นำกลับมาใช้ใหม่" + wra.get ()); } ลอง {thread.sleep (1); } catch (interruptedException e) {e.printStackTrace (); } system.out.println ("รายการ RQ:" + rq.poll ()); }} class A {@Override protected void finalize () โยน throwable {super.finalize (); System.out.println ("อยู่ในขั้นสุดท้าย"); - ## ผลลัพธ์ผลลัพธ์คือ:
วัตถุยังไม่ได้นำกลับมาใช้ใหม่ แต่รายการ@6276E1DBRQ: วัตถุ nulla เข้าสู่กระบวนการรวบรวมขยะในรายการสุดท้าย: java.lang.ref.weakreference@645064F
นี่เป็นการยืนยันคำสั่งข้างต้น
4.PhantomReference
เมื่อเปรียบเทียบกับการตรวจสอบหรือการอ้างอิงที่อ่อนแอความแตกต่างหลักในการถ่ายทอด phantomreference จะสะท้อนให้เห็นในประเด็นต่อไปนี้:
1. PhantomReference มีเพียงหนึ่ง phantomreference constructor (t Reference, referenceQueue <? super t> q) ดังนั้น phantomreference จะต้องใช้ร่วมกับ refermenteryQueue;
2. ไม่ว่าจะมีการอ้างอิงที่แข็งแกร่งไปยังวัตถุตัวบ่งชี้ที่ชี้ไปที่ phantomreference วิธีการ GET () ของ phantomreference ส่งคืนผลลัพธ์ที่เป็นโมฆะ
การอ้างอิงคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {a = new a (); ReferenceQueue <a> rq = new ReferenceQueue <a> (); phantomreference <a> pra = phantomreference ใหม่ <a> (a, rq); System.out.println ("pra.get ():" + pra.get ()); a = null; System.gc (); ลอง {thread.sleep (1); } catch (interruptedException e) {e.printStackTrace (); } system.out.println ("รายการ RQ:" + rq.poll ()); }} คลาส A {} ## ผลลัพธ์ผลลัพธ์คือ:
pra.get (): รายการ nullrq: java.lang.ref.phantomreference@1da12fc0
thread.sleep (1); ในรหัสฟังก์ชั่นเช่นเดียวกับในตัวอย่างข้างต้นและทั้งคู่ตรวจสอบให้แน่ใจว่าเธรดการรวบรวมขยะสามารถดำเนินการได้ มิฉะนั้นการอ้างอิงเสมือนจริงไปยังวัตถุตัวบ่งชี้ที่เข้าสู่กระบวนการรวบรวมขยะโดยไม่ต้องรวบรวมขยะจริงจะไม่ถูกเพิ่มเข้าไปใน phantomreference
เช่นเดียวกับความอ่อนแอ phantomreference ไม่ได้เปลี่ยนระยะเวลาการรวบรวมขยะของวัตถุที่ระบุ สามารถสรุปได้ว่าฟังก์ชั่นของ ReferenceQueue ส่วนใหญ่จะใช้เพื่อฟัง softreference/beakreference/phantomreference ระบุว่าวัตถุนั้นได้รับการรวบรวมขยะหรือไม่
ข้างต้นเป็นเนื้อหาเต็มรูปแบบของการวิเคราะห์ที่ครอบคลุมของประเภทอ้างอิง Java/Android และการใช้งานที่นำมาให้คุณโดยบรรณาธิการ ฉันหวังว่ามันจะเป็นประโยชน์กับคุณและสนับสนุน wulin.com เพิ่มเติม ~