คุณเคยใช้ PhantomReference ในโครงการใด ๆ หรือไม่?


90

สิ่งเดียวที่ฉันรู้PhantomReferenceคือ

  • หากคุณใช้get()วิธีนี้มันจะส่งคืนเสมอnullไม่ใช่วัตถุ มันใช้อะไร?
  • เมื่อใช้PhantomReferenceคุณต้องแน่ใจว่าวัตถุไม่สามารถคืนชีพจากfinalizeวิธีการได้

แต่การใช้แนวคิด / คลาสนี้คืออะไร?

คุณเคยใช้สิ่งนี้ในโครงการของคุณหรือคุณมีตัวอย่างที่เราควรใช้สิ่งนี้หรือไม่?


นอกจากนี้ยังstackoverflow.com/a/9827686/632951
Pacerier

เนื่องจากคุณไม่สามารถรับ obj ที่อ้างถึงของ PhantomReference ได้จึงเป็นการเรียกชื่อผิดโดยสมบูรณ์: ควรถูกเรียกFakeReferenceหรือNonReference.
Pacerier

นี่คือเธรด anothre ที่มีรหัส: stackoverflow.com/q/43311825/632951
Pacerier

คำตอบ:


47

ฉันใช้PhantomReferences ในตัวสร้างโปรไฟล์หน่วยความจำที่เรียบง่ายและเชี่ยวชาญมากเพื่อตรวจสอบการสร้างและการทำลายวัตถุ ฉันต้องการพวกมันเพื่อติดตามการทำลายล้าง แต่แนวทางนี้ล้าสมัย (เขียนขึ้นในปี 2004 โดยกำหนดเป้าหมายเป็น J2SE 1.4) เครื่องมือสร้างโปรไฟล์ระดับมืออาชีพนั้นมีประสิทธิภาพและเชื่อถือได้มากกว่าและคุณลักษณะ Java 5 ที่ใหม่กว่าเช่น JMX หรือเอเจนต์และ JVMTI ก็สามารถใช้งานได้เช่นกัน

PhantomReferences (ใช้ร่วมกับคิวอ้างอิงเสมอ) ดีกว่าfinalizeซึ่งมีปัญหาบางประการดังนั้นจึงควรหลีกเลี่ยง ส่วนใหญ่ทำให้วัตถุสามารถเข้าถึงได้อีกครั้ง สิ่งนี้สามารถหลีกเลี่ยงได้ด้วยสำนวนผู้พิทักษ์สุดท้าย (-> อ่านเพิ่มเติมใน 'Effective Java') ดังนั้นพวกเขาจึงเป็นขั้นสุดท้ายใหม่ด้วย

นอกจากนี้PhantomReferences

ช่วยให้คุณระบุได้ว่าเมื่อใดที่วัตถุถูกลบออกจากหน่วยความจำ ในความเป็นจริงพวกเขาเป็นวิธีเดียวที่จะตัดสินว่า โดยทั่วไปแล้วสิ่งนี้ไม่ได้มีประโยชน์ แต่อาจมีประโยชน์ในบางสถานการณ์เช่นการจัดการรูปภาพขนาดใหญ่หากคุณทราบแน่ชัดว่ารูปภาพควรเป็นขยะคุณสามารถรอจนกว่ารูปภาพนั้นจะเป็นจริงก่อนจึงจะลองโหลดรูปภาพถัดไป ดังนั้นจึงทำให้ OutOfMemoryError ที่น่ากลัวมีโอกาสน้อยลง (อ้างจากenicholas )

และเป็นPSDเขียนครั้งแรก Roedy สีเขียวมีสรุปที่ดีของการอ้างอิง


21

ทั่วไปหั่นสี่เหลี่ยมลูกเต๋าขึ้นคำอธิบายตารางจาก Java คำศัพท์

ซึ่งแน่นอนว่าเกิดขึ้นพร้อมกับเอกสาร PhantomReference :

วัตถุอ้างอิง Phantom ซึ่งจัดทำขึ้นหลังจากตัวรวบรวมกำหนดว่าการอ้างอิงของพวกเขาอาจถูกเรียกคืน การอ้างอิง Phantom มักใช้สำหรับการจัดตารางเวลาการดำเนินการล้างข้อมูลก่อนการฝังศพด้วยวิธีที่ยืดหยุ่นกว่าที่เป็นไปได้ด้วยกลไกการสรุปผล Java

และสุดท้าย แต่ไม่น้อยทุกรายละเอียดที่เต็มไปด้วยเลือด ( นี่คือการอ่านที่ดี ): Java อ้างอิงวัตถุ (หรือฉันเรียนรู้ที่จะเลิกกังวลและความรัก OutOfMemoryError)

มีความสุขในการเขียนโค้ด (แต่เพื่อตอบคำถามฉันเคยใช้ WeakReferences เท่านั้น)


Btw คำถามเกี่ยวกับบทความนั้น ในส่วนของ PhantomReference เขายังคงอ้างอิงถึงวัตถุการเชื่อมต่อผ่านทั้งสองตาราง หมายความว่าการเชื่อมต่อจะไม่มีวันเข้าถึงไม่ได้ (สมมติว่าอินสแตนซ์พูลนั้นไม่สามารถเข้าถึงได้) ดังนั้น PhantomReferences ที่เกี่ยวข้องจะไม่ถูกบังคับใช่ไหม? หรือฉันขาดอะไรไป?
shrini1000

1
ว้าวบทความจาก kdgregory สมควรได้รับ +10
Pacerier

14

คำอธิบายที่ยอดเยี่ยมเกี่ยวกับการใช้งาน Phantom Reference:

การอ้างอิง Phantom เป็นวิธีที่ปลอดภัยในการทราบว่าวัตถุถูกลบออกจากหน่วยความจำ ตัวอย่างเช่นพิจารณาแอปพลิเคชันที่เกี่ยวข้องกับรูปภาพขนาดใหญ่ สมมติว่าเราต้องการโหลดภาพขนาดใหญ่ในหน่วยความจำเมื่อภาพขนาดใหญ่อยู่ในหน่วยความจำแล้วซึ่งพร้อมสำหรับการเก็บขยะ ในกรณีเช่นนี้เราต้องการรอจนกว่าภาพเก่าจะถูกรวบรวมก่อนที่จะโหลดใหม่ ที่นี่การอ้างอิงผีเป็นตัวเลือกที่ยืดหยุ่นและปลอดภัยในการเลือก การอ้างอิงของรูปภาพเก่าจะถูกกำหนดไว้ใน ReferenceQueue เมื่อออบเจ็กต์ภาพเก่าเสร็จสิ้น หลังจากได้รับการอ้างอิงแล้วเราสามารถโหลดภาพใหม่ลงในหน่วยความจำได้


12

ฉันพบกรณีการใช้งานที่เป็นประโยชน์และเป็นประโยชน์PhantomReferenceซึ่งอยู่org.apache.commons.io.FileCleaningTrackerในโครงการ commons-io FileCleaningTrackerจะลบไฟล์ฟิสิคัลเมื่อวัตถุเครื่องหมายถูกรวบรวมขยะ
สิ่งที่ควรทราบคือTrackerชั้นเรียนที่ขยายPhantomReferenceชั้นเรียน


5

สิ่งนี้ควรจะเลิกใช้กับ JAVA 9 แล้ว!
ใช้java.util.Cleanerแทน! (หรือsun.misc.Cleanerบน JRE รุ่นเก่า)

โพสต์ต้นฉบับ:


ฉันพบว่าการใช้ PhantomReferences มีข้อผิดพลาดเกือบเท่ากันกับวิธี Finalizer (แต่จะมีปัญหาน้อยลงเมื่อคุณทำให้ถูกต้อง) ฉันได้เขียนโซลูชันขนาดเล็ก (เฟรมเวิร์กขนาดเล็กมากที่จะใช้ PhantomReferences) สำหรับ Java 8 ซึ่งอนุญาตให้ใช้นิพจน์แลมบ์ดาเป็นการเรียกกลับเพื่อเรียกใช้หลังจากที่วัตถุถูกลบ คุณสามารถลงทะเบียนการเรียกกลับสำหรับทรัพยากรภายในที่ควรปิด ด้วยสิ่งนี้ฉันได้พบวิธีแก้ปัญหาที่เหมาะกับฉันเพราะมันทำให้มันใช้งานได้จริงมากขึ้น

https://github.com/claudemartin/java-cleanup

นี่คือตัวอย่างเล็ก ๆ เพื่อแสดงวิธีการลงทะเบียนการโทรกลับ:

  class Foo implements Cleanup {
    //...  
    public Foo() { 
    //...    
      this.registerCleanup((value) -> {
        try {
          // 'value' is 'this.resource'
          value.close();
        } catch (Exception e) {
          logger.warning("closing resource failed", e);
        }
      }, this.resource);
    }

จากนั้นมีวิธีที่ง่ายกว่าสำหรับการปิดอัตโนมัติโดยทำเช่นเดียวกับด้านบน:

this.registerAutoClose(this.resource);

เพื่อตอบคำถามของคุณ:

[แล้วมันใช้อะไร]

คุณไม่สามารถล้างบางสิ่งที่ไม่มีอยู่จริงได้ แต่อาจมีทรัพยากรที่ยังคงมีอยู่และจำเป็นต้องได้รับการทำความสะอาดเพื่อให้สามารถลบออกได้

แต่การใช้แนวคิด / คลาสนี้คืออะไร?

ไม่จำเป็นต้องทำอะไรที่มีผลกระทบอื่นใดนอกจากการดีบัก / การบันทึก หรืออาจจะเป็นสถิติ ฉันเห็นว่ามันเหมือนบริการแจ้งเตือนจาก GC มากกว่า คุณยังสามารถใช้เพื่อลบข้อมูลรวมที่ไม่เกี่ยวข้องเมื่อลบออบเจ็กต์ (แต่อาจมีวิธีแก้ไขที่ดีกว่านี้) ตัวอย่างมักกล่าวถึงการเชื่อมต่อฐานข้อมูลที่จะปิด แต่ฉันไม่เห็นว่านี่เป็นความคิดที่ดีอย่างไรเนื่องจากคุณไม่สามารถทำงานกับธุรกรรมได้ กรอบแอปพลิเคชันจะให้ทางออกที่ดีกว่ามากสำหรับสิ่งนั้น

คุณเคยใช้สิ่งนี้ในโครงการของคุณหรือคุณมีตัวอย่างที่เราควรใช้สิ่งนี้หรือไม่? หรือแนวคิดนี้ทำขึ้นเพื่อมุมมองการสัมภาษณ์เท่านั้น;)

ฉันใช้มันเป็นส่วนใหญ่เพื่อการบันทึก ดังนั้นฉันจึงสามารถติดตามองค์ประกอบที่ถูกลบออกและดูว่า GC ทำงานอย่างไรและสามารถปรับแต่งได้ ฉันจะไม่เรียกใช้โค้ดที่สำคัญด้วยวิธีนี้ หากจำเป็นต้องปิดบางสิ่งก็ควรทำในคำสั่ง try-with-resource-statement และฉันใช้มันในการทดสอบหน่วยเพื่อให้แน่ใจว่าฉันไม่มีหน่วยความจำรั่วไหล แบบเดียวกับที่ jontejj ทำ แต่วิธีการแก้ปัญหาของฉันค่อนข้างกว้างกว่าเล็กน้อย


ใช่เช่นเดียวกับโซลูชัน "java-cleanup" ของฉัน ทั้งสองอย่างเป็นนามธรรมดังนั้นคุณไม่จำเป็นต้องจัดการกับสิ่งเหล่านี้โดยตรง
Claude Martin

3

ฉันใช้ PhantomReference ในการทดสอบหน่วยเพื่อตรวจสอบว่ารหัสที่อยู่ระหว่างการทดสอบไม่ได้เก็บการอ้างอิงที่ไม่เป็นระเบียบกับวัตถุบางอย่าง ( รหัสเดิม )

import static com.google.common.base.Preconditions.checkNotNull;
import static org.fest.assertions.Assertions.assertThat;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

import com.google.common.testing.GcFinalization;

/**
* Helps to test for memory leaks
*/
public final class MemoryTester
{
private MemoryTester()
{
}

/**
* A simple {@link PhantomReference} that can be used to assert that all references to it is
* gone.
*/
public static final class FinalizationAwareObject extends PhantomReference<Object>
{
private final WeakReference<Object> weakReference;

private FinalizationAwareObject(Object referent, ReferenceQueue<Object> referenceQueue)
{
super(checkNotNull(referent), referenceQueue);
weakReference = new WeakReference<Object>(referent, referenceQueue);
}

/**
* Runs a full {@link System#gc() GC} and asserts that the reference has been released
* afterwards
*/
public void assertThatNoMoreReferencesToReferentIsKept()
{
String leakedObjectDescription = String.valueOf(weakReference.get());
GcFinalization.awaitFullGc();
assertThat(isEnqueued()).as("Object: " + leakedObjectDescription + " was leaked").isTrue();
}
}

/**
* Creates a {@link FinalizationAwareObject} that will know if {@code referenceToKeepTrackOff}
* has been garbage collected. Call
* {@link FinalizationAwareObject#assertThatNoMoreReferencesToReferentIsKept()} when you expect
* all references to {@code referenceToKeepTrackOff} be gone.
*/
public static FinalizationAwareObject createFinalizationAwareObject(Object referenceToKeepTrackOff)
{
return new FinalizationAwareObject(referenceToKeepTrackOff, new ReferenceQueue<Object>());
}
}

และการทดสอบ :

@Test
public void testThatHoldingOnToAnObjectIsTreatedAsALeak() throws Exception
{
    Object holdMeTight = new String("Hold-me-tight");
    FinalizationAwareObject finalizationAwareObject = MemoryTester.createFinalizationAwareObject(holdMeTight);
    try
    {
    finalizationAwareObject.assertThatNoMoreReferencesToReferentIsKept();
    fail("holdMeTight was held but memory leak tester did not discover it");
    }
    catch(AssertionError expected)
    {
    assertThat(expected).hasMessage("[Object: Hold-me-tight was leaked] expected:<[tru]e> but was:<[fals]e>");
    }
}

2

เป็นเรื่องปกติที่จะใช้ในWeakReferenceที่ที่PhantomReferenceเหมาะสมกว่า วิธีนี้หลีกเลี่ยงปัญหาบางประการในการคืนชีพอ็อบเจ็กต์หลังจากที่WeakReferenceตัวเก็บรวบรวมขยะล้าง / จัดคิว โดยปกติความแตกต่างไม่สำคัญเพราะผู้คนไม่ได้เล่นบั๊กเกอร์โง่ ๆ

การใช้PhantomReferenceมีแนวโน้มที่จะรบกวนมากกว่าเล็กน้อยเนื่องจากคุณไม่สามารถแสร้งทำเป็นว่าgetวิธีนี้ใช้ได้ผล ตัวอย่างเช่นคุณไม่สามารถเขียนไฟล์Phantom[Identity]HashMap.


IdentityHashMap <PhantomReference> เป็นหนึ่งในตำแหน่งที่เหมาะสมสำหรับ IdentityHashMap โปรดทราบว่าการอ้างอิงที่แข็งแกร่งเก็บไว้ให้ PhantomReference ที่แต่ไม่อ้างอิง

คุณหมายถึงว่าสำหรับการอ้างอิงที่อ่อนแอการสรุปขั้นสุดท้ายอาจสร้าง obj ขึ้นมาใหม่หรือไม่? weakref.getสามารถกลับมาnullและหลังจากนั้นก็ยังสามารถคืน obj ได้?
Pacerier

@Pacerier finalizeไม่ได้สร้างวัตถุขึ้นใหม่เช่นนี้ มันสามารถทำให้วัตถุที่สามารถเข้าถึงได้อย่างมากอีกครั้งหลังจากที่WeakReferenceผลตอบแทนnullจากgetและ enqueued / (user166390: เช่นเดียวกับในแผนที่ที่กำหนดเป้าหมายของการอ้างอิงเช่นเดียวกับWeakHashMapไม่ใช่แผนที่ระบุตัวตนของการอ้างอิงซึ่งใช้ได้)
Tom Hawtin - แท็

1

หากคุณใช้เมธอด get () มันจะคืนค่า null เสมอไม่ใช่อ็อบเจกต์ [แล้วมันใช้อะไร]

วิธีการที่เป็นประโยชน์ต่อการเรียกร้อง (มากกว่าget()) จะเป็นหรือisEnqueued() referenceQueue.remove()คุณจะเรียกวิธีการเหล่านี้เพื่อดำเนินการบางอย่างที่จำเป็นต้องเกิดขึ้นในรอบสุดท้ายของการรวบรวมขยะของวัตถุ

ครั้งแรกคือเมื่อวัตถุมีfinalize()การเรียกใช้เมธอดดังนั้นคุณสามารถใส่ตะขอปิดไว้ที่นั่นได้ อย่างไรก็ตามตามที่คนอื่น ๆ กล่าวไว้อาจมีวิธีที่แน่นอนมากขึ้นในการทำความสะอาดหรือการดำเนินการใด ๆ ที่จำเป็นต้องเกิดขึ้นก่อนและหลังการเก็บขยะหรือโดยทั่วไปแล้วเมื่อวัตถุสิ้นอายุการใช้งาน


1

ฉันพบการใช้งานจริงอีกครั้งPhantomReferencesในชั้นLeakDetectorของ Jetty

Jetty ใช้LeakDetectorคลาสเพื่อตรวจสอบว่ารหัสไคลเอนต์ได้รับทรัพยากร แต่ไม่เคยเผยแพร่และLeakDetectorคลาสใช้PhantomReferencesเพื่อวัตถุประสงค์นี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.