2009-10-21 28 views
69

Điều duy nhất tôi biết về PhantomReference là,Bạn đã từng sử dụng PhantomReference trong bất kỳ dự án nào chưa?

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

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

Bạn đã từng sử dụng điều này trong bất kỳ dự án nào của mình hay bạn có ví dụ nào về việc chúng tôi nên sử dụng điều này không?

+0

Cũng https://stackoverflow.com/a/9827686/632951 – Pacerier

+0

Vì bạn không thể lấy obj được giới thiệu của PhantomReference, một từ sai hoàn toàn của nó: Nó phải được gọi là 'FakeReference' hoặc' NonReference'. – Pacerier

+0

Đây là chuỗi anothre có mã: https://stackoverflow.com/q/43311825/632951 – Pacerier

Trả lời

42

Tôi đã sử dụng PhantomReference s trong một simplistic, very specialized kind of memory profiler để theo dõi việc tạo và hủy đối tượng. Tôi cần chúng để theo dõi sự hủy diệt. 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ược tả chuyên nghiệp mạnh hơn và đáng tin cậy hơn và các tính năng Java 5 mới hơn như JMX hoặc các agent và JVMTI cũng có thể được sử dụng cho nó.

PhantomReference s (luôn được sử dụng cùng với hàng đợi Tham chiếu) vượt trội hơn finalize 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ể truy cập một lần nữa. Điều này có thể tránh được với thành ngữ người giám hộ finalizer (-> đọc thêm trong 'Effective Java'). Vì vậy, họ cũng là mới hoàn thành.

Hơn nữa, PhantomReference s

cho phép bạn xác định chính xác khi một đối tượng đã bị xóa khỏi bộ nhớ. Họ thực tế là cách duy nhất để xác định điều đó. Điều này thường 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 hình ảnh lớn: nếu bạn biết chắc chắn rằng hình ảnh phải được thu gom , bạn có thể đợi cho đến khi nó thực sự là trước khi cố gắng tải hình ảnh tiếp theo, và do đó làm cho OutOfMemoryError sợ hãi ít hơn khả năng. (Được trích dẫn từ enicholas.)

Và như psd đã viết trước, Roedy Green có good summary of references.

18

A general diced-up table explanation, từ Thuật ngữ Java.

Trong đó tất nhiên trùng với PhantomReference documentation:

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

Và cuối cùng nhưng không kém, tất cả các chi tiết đẫm máu (đây là một tốt đọc): Java Reference Objects (or How I Learned to Stop Worrying and Love OutOfMemoryError).

Mã hóa vui vẻ. (Nhưng để trả lời câu hỏi, tôi đã từng sử dụng WeakReferences.)

+0

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

+0

Ồ, bài viết đó từ kdgregory xứng đáng là +10 – Pacerier

2

Thông thường sử dụng WeakReference trong đó PhantomReference phù hợp hơn. Điều này tránh được một số vấn đề nhất định khi có thể hồi sinh các đối tượng sau khi WeakReference được xóa/enqueued bởi bộ thu gom rác. 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 các trình giả lập ngớ ngẩn.

Sử dụng PhantomReference có xu hướng xâm phạm hơn một chút vì bạn không thể giả vờ rằng phương thức get hoạt động. Ví dụ: bạn không thể viết Phantom[Identity]HashMap.

+0

IdentityHashMap thực sự là một trong những nơi thích hợp cho một IdentityHashMap. Lưu ý rằng * tham chiếu mạnh mẽ * được lưu vào PhantomReference, * nhưng không được tham chiếu *. –

+0

Hay tôi đã đọc hoàn toàn sai? Tôi xin lỗi nếu tôi đã làm. –

+0

Bạn có thực sự có nghĩa là để tham khảo yếu, hoàn thành có thể tái tạo obj? 'weakref.get' có thể trả về' null', và sau đó, nó vẫn có thể trả về obj? – Pacerier

9

Tôi đã tìm thấy trường hợp sử dụng thực tế và hữu ích của PhantomReferenceorg.apache.commons.io.FileCleaningTracker trong dự án commons-io. FileCleaningTracker sẽ xóa tệp vật lý khi đối tượng điểm đánh dấu của nó là rác được thu thập.
Điều cần lưu ý là lớp Tracker mở rộng lớp PhantomReference.

3

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

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:

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

Great explanation của Phantom sử dụng tham khảo:

tài liệu tham khảo Phantom là cách an toàn để biết một đối tượng đã bị xoá từ bộ nhớ. Ví dụ: hãy xem xét một ứng dụng đề cập đến hình ảnh lớn. Giả sử rằng chúng tôi 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 cho rác được thu thập. Trong trường hợp này, chúng tôi muốn đợi cho đến khi hình ảnh cũ là được thu thập trước khi tải hình mới. Ở đây, tham chiếu ảo là tùy chọn linh hoạt và an toàn để chọn. Các tham chiếu của hình ảnh cũ sẽ được enqueued trong ReferenceQueue khi đối tượng hình ảnh cũ là hoàn thành. 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ớ.

3

NÀY NÊN LÀ BẤT HỢP VỚI JAVA 9!
Sử dụng java.util.Cleaner để thay thế! (Hoặc sun.misc.Cleaner trên JRE cũ)

Original post:


tôi thấy rằng việc sử dụng các PhantomReferences có gần cùng một lượng cạm bẫy như các phương pháp finalizer (nhưng một ít vấn đề hơn khi bạn làm cho nó đúng). Tôi đã viết một giải pháp nhỏ (một khung công tác rất nhỏ để sử dụng PhantomReferences) cho Java 8. Nó cho phép sử dụng các biểu thức lambda như các cuộc gọi lại để chạy sau khi đối tượng đã được gỡ bỏ. Bạn có thể đăng ký cuộc 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 thấy 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ỏ để chứng tỏ một callback được đăng ký:

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 đó là phương pháp đơn giản hơn để tự động đóng, làm về giống như trên:

this.registerAutoClose(this.resource); 

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

[sau đó sử dụng nó]

Bạn không thể dọn sạch thứ gì đó không tồn tại. Nhưng nó có thể có nguồn tài nguyên vẫn tồn tại và cần phải được làm sạch để chúng có thể được loại bỏ.

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

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 khác ngoài gỡ lỗi/ghi nhật ký. Hoặc có thể cho thống kê. Tôi thấy nó giống như một dịch vụ thông báo từ GC. Bạn cũng có thể muốn sử dụng nó để xóa dữ liệu tổng hợp không liên quan khi đối tượng bị xóa (nhưng có thể có giải pháp tốt hơn cho điều đó). Ví dụ thường đề cập đến các kết nối cơ sở dữ liệu bị đóng, nhưng tôi không thấy đây là một ý tưởng hay như 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 cho điều đó.

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

Tôi sử dụng chủ yếu chỉ để ghi nhật ký. Vì vậy, tôi có thể theo dõi các yếu tố đã xóa 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 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 tuyên bố 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 có bất kỳ rò rỉ bộ nhớ nào. Cách tương tự như jontejj làm điều đó. Nhưng giải pháp của tôi là tổng quát hơn một chút.

+0

Vâng,' java.util.Cleaner' sử dụng nội bộ PhantomReferences anyway ... – Pacerier

+0

Vâng, giống như giải pháp của tôi "dọn dẹp java". Cả hai đều trừu tượng, vì vậy bạn không cần phải đối phó trực tiếp với họ. –

1

nếu bạn sử dụng phương thức get() của nó, nó sẽ luôn trả về giá trị rỗng, chứ không phải đối tượng . [Sau đó whats sử dụng nó]

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

Lần đầu tiên xung quanh là khi đối tượng có phương thức finalize() đượ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ói, có lẽ có nhiều cách chắc chắn hơn để thực hiện làm sạch hoặc bất kỳ hành động cần phải diễn ra trước và sau thu gom rác thải, hoặc nói chung, khi kết thúc của cuộc sống của đối tượng.

Các vấn đề liên quan