Bạn đã từng sử dụng PhantomReference trong bất kỳ dự án nào chưa?


89

Điều duy nhất tôi biết PhantomReferencelà,

  • Nếu bạn sử dụng get()phương thức của nó , nó sẽ luôn trả về nullchứ không phải đối tượng. Công dụng của nó là gì?
  • Bằng cách sử dụng PhantomReference, bạn đảm bảo rằng đối tượng không thể hồi sinh từ finalizephương thức.

Nhưng việc sử dụng khái niệm / lớp này là gì?

Bạn đã bao giờ sử dụng cái này trong bất kỳ dự án nào của mình chưa hoặc bạn có ví dụ nào mà chúng ta nên sử dụng cái này không?



Vì bạn không thể nhận được đối tượng được giới thiệu của PhantomReference, nên nó hoàn toàn là một từ nhầm lẫn: Nó nên được gọi là FakeReferencehoặc NonReference.
Pacerier

Dưới đây là anothre chủ đề với mã: stackoverflow.com/q/43311825/632951
Pacerier

Câu trả lời:


47

Tôi đã sử dụng PhantomReferences trong một loại hồ sơ bộ nhớ đơn giản, rất chuyên dụng để theo dõi việc tạo và phá hủy đối tượng. Tôi cần chúng để theo dõi sự tàn phá. Nhưng cách tiếp cận đã lỗi thời. (Nó được viết vào năm 2004 nhắm mục tiêu J2SE 1.4.) Các công cụ lập hồ sơ chuyên nghiệp mạnh hơn và đáng tin cậy hơn nhiều và các tính năng Java 5 mới hơn như JMX hoặc các tác nhân và JVMTI cũng có thể được sử dụng cho việc đó.

PhantomReferences (luôn được sử dụng cùng với hàng đợi Tham chiếu) là ưu việt hơn so với finalizenó có một số vấn đề và do đó nên tránh. Chủ yếu là làm cho các đối tượng có thể tiếp cận lại. Điều này có thể tránh được với thành ngữ người giám hộ cuối cùng (-> đọc thêm trong 'Java hiệu quả'). Vì vậy, họ cũng là người cuối cùng mới .

Hơn nữa, PhantomReferences

cho phép bạn xác định chính xác thời điểm một đối tượng bị xóa khỏi bộ nhớ. Trên thực tế, chúng là cách duy nhất để xác định điều đó. Điều này nói chung không hữu ích, nhưng có thể hữu ích trong một số trường hợp rất cụ thể như thao tác với các hình ảnh lớn: nếu bạn biết chắc chắn rằng một hình ảnh nên được thu gom rác, bạn có thể đợi cho đến khi nó thực sự như vậy trước khi cố gắng tải hình ảnh tiếp theo , và do đó làm cho Lỗi OutOfMemoryError đáng sợ ít xảy ra hơn. (Trích từ enicholas .)

Và như psd đã viết đầu tiên, Roedy Green có một bản tóm tắt tài liệu tham khảo tốt .


21

Một chung thái hạt lựu lên bảng giải thích , từ Java Tự điển.

Tất nhiên điều nào trùng với tài liệu PhantomReference :

Các đối tượng tham chiếu ma, được xếp vào hàng sau khi người thu thập xác định rằng các đối tượng tham chiếu của chúng có thể được lấy lại. Tham chiếu Phantom thường được sử dụng nhất để lên lịch cho các hành động dọn dẹp trước khi giết mổ theo cách linh hoạt hơn khả năng có thể với cơ chế hoàn thiện Java.

Và cuối cùng nhưng không kém phần quan trọng, tất cả các chi tiết đẫm máu ( đây là một bài đọc hay ): Đối tượng tham chiếu Java (hoặc Cách tôi đã học để ngừng lo lắng và yêu thích OutOfMemoryError) .

Chúc bạn viết mã vui vẻ. (Nhưng để trả lời câu hỏi, tôi chỉ sử dụng WeakRefferences.)


Btw, một câu hỏi về bài báo đó. Trong phần trên PhantomReference, anh ấy giữ một tham chiếu mạnh mẽ đến các đối tượng kết nối thông qua hai bảng đó. Nó có nghĩa là các kết nối sẽ không bao giờ trở nên không thể truy cập được (giả sử bản thân cá thể nhóm không bao giờ trở nên không thể truy cập được). Vì vậy, các PhantomRefferences tương ứng sẽ không bao giờ được xếp vào hàng, phải không? Hay tôi đang thiếu cái gì đó?
shni1000

1
Chà, bài báo đó từ kdgregory xứng đáng được +10
Pacerier.

14

Giải thích tuyệt vời về cách sử dụng Phantom Reference:

Tham chiếu Phantom là cách an toàn để biết một đối tượng đã bị xóa khỏi bộ nhớ. Ví dụ: hãy xem xét một ứng dụng xử lý hình ảnh lớn. Giả sử rằng chúng ta muốn tải một hình ảnh lớn vào bộ nhớ khi hình ảnh lớn đã có trong bộ nhớ đã sẵn sàng để thu dọn rác. Trong trường hợp này, chúng tôi muốn đợi cho đến khi hình ảnh cũ được thu thập trước khi tải hình ảnh mới. Ở đây, tham chiếu ma là tùy chọn linh hoạt và an toàn để lựa chọn. Tham chiếu của hình ảnh cũ sẽ được xếp vào hàng trong ReferenceQueue sau khi đối tượng hình ảnh cũ được hoàn thiện. Sau khi nhận được tham chiếu đó, chúng tôi có thể tải hình ảnh mới vào bộ nhớ.


12

Tôi đã tìm thấy một trường hợp sử dụng thực tế và hữu ích PhantomReferencenằm org.apache.commons.io.FileCleaningTrackertrong dự án commons-io. FileCleaningTrackersẽ xóa tệp vật lý khi đối tượng đánh dấu của nó được thu gom rác.
Một điều cần lưu ý là Trackerlớp mở rộng PhantomReferencelớp.


5

ĐIỀU NÀY NÊN CÙNG JAVA 9 NHÉ!
Sử dụng java.util.Cleanerthay thế! (Hoặc sun.misc.Cleanertrên JRE cũ hơn)

Bài gốc:


Tôi nhận thấy rằng việc sử dụng PhantomRefferences có gần như cùng một số vấn đề như các phương pháp hoàn thiện (nhưng sẽ ít vấn đề hơn khi bạn hiểu đúng). Tôi đã viết một giải pháp nhỏ (một khuôn khổ rất nhỏ để sử dụng PhantomRefferences) cho Java 8. Nó cho phép sử dụng các biểu thức lambda làm lệnh gọi lại để chạy sau khi đối tượng đã bị loại bỏ. Bạn có thể đăng ký các lệnh gọi lại cho các tài nguyên bên trong cần được đóng lại. Với điều này, tôi đã tìm ra một giải pháp phù hợp với tôi vì nó làm cho nó thực tế hơn nhiều.

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

Dưới đây là một ví dụ nhỏ cho thấy cách đăng ký gọi lại:

  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);
    }

Và sau đó, có một phương pháp đơn giản hơn để tự động đóng, thực hiện tương tự như ở trên:

this.registerAutoClose(this.resource);

Để trả lời câu hỏi của bạn:

[thì việc sử dụng nó là gì]

Bạn không thể dọn dẹp những thứ không tồn tại. Nhưng nó có thể có các tài nguyên vẫn tồn tại và cần được dọn dẹp để có thể loại bỏ chúng.

Nhưng việc sử dụng khái niệm / lớp này là gì?

Nó không nhất thiết phải làm bất cứ điều gì với bất kỳ hiệu ứng nào ngoài gỡ lỗi / ghi nhật ký. Hoặc có thể để thống kê. Tôi thấy nó giống một dịch vụ thông báo từ GC hơn. Bạn cũng có thể muốn sử dụng nó để xóa dữ liệu tổng hợp trở nên không liên quan sau khi đối tượng bị xóa (nhưng có lẽ có những giải pháp tốt hơn cho điều đó). Các ví dụ thường đề cập đến việc đóng các kết nối cơ sở dữ liệu, nhưng tôi không thấy đây là một ý tưởng hay vì bạn không thể làm việc với các giao dịch. Một khung ứng dụng sẽ cung cấp một giải pháp tốt hơn nhiều cho điều đó.

Bạn đã bao giờ sử dụng cái này trong bất kỳ dự án nào của mình chưa, hoặc bạn có ví dụ nào mà chúng ta nên sử dụng cái này không? Hay là khái niệm này được tạo ra chỉ cho quan điểm phỏng vấn;)

Tôi sử dụng nó chủ yếu chỉ để ghi nhật ký. Vì vậy, tôi có thể theo dõi các phần tử bị loại bỏ và xem cách GC hoạt động và có thể được tinh chỉnh. Tôi sẽ không chạy bất kỳ mã quan trọng nào theo cách này. Nếu một cái gì đó cần phải được đóng lại thì nó nên được thực hiện trong một câu lệnh try-with-resource-. Và tôi sử dụng nó trong các bài kiểm tra đơn vị, để đảm bảo rằng tôi không bị rò rỉ bộ nhớ. Cách làm tương tự như jontejj. Nhưng giải pháp của tôi là tổng quát hơn một chút.


Có, giống như giải pháp của tôi "java-cleanup". Cả hai đều là trừu tượng, vì vậy bạn không cần phải giải quyết chúng trực tiếp.
Claude Martin

3

Tôi đã sử dụng PhantomReference trong một bài kiểm tra đơn vị để xác minh rằng mã được kiểm tra không giữ các tham chiếu không cần thiết đến một số đối tượng. ( Mã gốc )

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>());
}
}

bài kiểm tra :

@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

Thông thường thì sử dụng WeakReferenceở đâu PhantomReferencethích hợp hơn. Điều này tránh một số vấn đề nhất định về khả năng phục hồi các đối tượng sau khi WeakReferencebộ thu gom rác xóa / xếp hàng. Thông thường, sự khác biệt không quan trọng bởi vì mọi người không chơi những kẻ phá bĩnh ngớ ngẩn.

Việc sử dụng PhantomReferencecó xu hướng xâm nhập hơn một chút vì bạn không thể giả vờ rằng getphương pháp này hoạt động. Ví dụ, bạn không thể viết a Phantom[Identity]HashMap.


IdentityHashMap <PhantomReference> thực sự là một trong những nơi thích hợp cho IdentityHashMap. Lưu ý rằng tham chiếu mạnh được giữ cho PhantomReference, nhưng không phải tham chiếu .

Ý bạn thực sự là đối với các tham chiếu yếu, việc hoàn thiện có thể tạo lại đối tượng? weakref.getcó thể trở lại null, và sau đó, nó vẫn có thể trả lại obj?
Pacerier

@Pacerier finalizekhông tạo lại đối tượng như vậy. Nó có thể làm cho đối tượng có thể được tiếp cận mạnh mẽ trở lại sau khi WeakReferencetrả về nulltừ getvà được xếp hàng. / (User166390: Như trong bản đồ keyed trên mục tiêu của các tài liệu tham khảo, như WeakHashMapkhông, không phải là một bản đồ sắc của tài liệu tham khảo mà là tốt.)
Tom Hawtin - tackline

1

nếu bạn sử dụng phương thức get () của nó, nó sẽ luôn trả về null chứ không phải đối tượng. [thì việc sử dụng nó là gì]

Các phương thức hữu ích để gọi (thay vì get()) sẽ là isEnqueued()hoặc referenceQueue.remove(). Bạn sẽ gọi các phương thức này để thực hiện một số hành động cần diễn ra trong vòng thu gom rác cuối cùng của đối tượng.

Lần đầu tiên là khi đối tượng có finalize()phương thức của nó được gọi, vì vậy bạn cũng có thể đặt các móc đóng ở đó. Tuy nhiên, như những người khác đã nêu, có lẽ có nhiều cách chắc chắn hơn để thực hiện dọn dẹp hoặc bất kỳ hành động nào cần thực hiện trước và sau khi thu gom rác hoặc nói chung là khi hết tuổi thọ của đối tượng.


1

Tôi đã tìm thấy một cách sử dụng thực tế khác PhantomReferencestrong lớp LeakDetector của Jetty.

Jetty sử dụng LeakDetectorlớp để phát hiện nếu mã máy khách có được một tài nguyên nhưng không bao giờ giải phóng nó và LeakDetectorlớp sử dụng PhantomReferencescho mục đích này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.