2009-08-25 30 views
29

Làm thế nào để ngăn chặn một đối tượng bị thu gom rác?Làm thế nào để ngăn chặn một đối tượng bị thu gom rác?

Có cách tiếp cận nào bằng cách hoàn thành hoặc tham chiếu ảo hoặc bất kỳ cách tiếp cận nào khác không?

Tôi đã được hỏi câu hỏi này trong một cuộc phỏng vấn. Người phỏng vấn cho rằng có thể sử dụng finalize().

+0

Vui lòng cung cấp một số đoạn mã –

+0

Rahul, được bạn đạo "Vui lòng cung cấp một số đoạn mã" đối với bất cứ ai đặc biệt, hoặc là bạn có ý nghĩa để cập nhật các yêu cầu của câu hỏi của bạn? –

+0

Hey i cập nhật ques của tôi –

Trả lời

32

Giữ tham chiếu. Nếu đối tượng của bạn đang được thu thập sớm, nó là một triệu chứng mà bạn có một lỗi trong việc thiết kế các ứng dụng của bạn.

Bộ thu gom rác chỉ thu thập các đối tượng không có tham chiếu trong ứng dụng của bạn. Nếu không có đối tượng nào tự nhiên tham khảo đối tượng đã thu thập, hãy tự hỏi tại sao nó phải được giữ sống.

Một ứng dụng mà bạn thường không có tham chiếu, nhưng muốn giữ một đối tượng là một singleton. Trong trường hợp này, bạn có thể sử dụng biến tĩnh. Một thi thể của một singleton sẽ trông như thế này:

public class Singleton { 
    private static Singleton uniqueInstance; 

    private Singleton() { 
    } 

    public static synchronized Singleton getInstance() { 
    if (uniqueInstance == null) { 
     uniqueInstance = new Singleton(); 
    } 
    return uniqInstance; 
    } 
} 

Edit: Về mặt kỹ thuật, bạn có thể lưu trữ một tham chiếu ở đâu đó trong finalizer của bạn. Điều này sẽ ngăn chặn các đối tượng được thu thập cho đến khi các nhà sưu tập xác định một lần nữa rằng không có tài liệu tham khảo nhiều hơn nữa. Trình hoàn thiện sẽ chỉ được gọi nhiều nhất một lần, tuy nhiên, vì vậy bạn phải đảm bảo rằng đối tượng của bạn (bao gồm cả các siêu lớp của nó) không cần phải được hoàn thành sau bộ sưu tập đầu tiên. Tuy nhiên, tôi khuyên bạn không nên sử dụng kỹ thuật này trong các chương trình thực tế. (Nó sẽ để lại cho các đồng nghiệp như tôi la hét WTF !?;)

protected void finalize() throws Throwable { 
    MyObjectStore.getInstance().store(this); 
    super.finalize(); // questionable, but you should ensure calling it somewhere. 
    } 
+0

Có cách nào nó có thể được thực hiện trong phương thức hoàn thiện –

+2

Bạn có thể kích động các đối tượng khác để lưu trữ một tham chiếu mới cho đối tượng của bạn. Lưu ý, tuy nhiên, rằng finalize được gọi nhiều nhất một lần cho bất kỳ đối tượng nhất định, vì vậy nó sẽ không được hoàn thành một lần nữa khi thu gom xác định rằng nó có thể được thu thập một lần nữa. – Tobias

+0

"(Nó sẽ để lại các đồng nghiệp như tôi hét lên WTF !?;)" Có tôi knw nhưng nó đã được hỏi bởi tôi bởi một người phỏng vấn vì vậy tôi không có bất kỳ ý tưởng tại sao ông hỏi tôi câu hỏi này :) –

3

Nếu vẫn còn là một tham chiếu đến đối tượng, nó sẽ không được thu gom rác thải. Nếu không có bất kỳ tham chiếu đến nó, bạn không nên quan tâm.

Nói cách khác - bộ thu gom rác chỉ thu thập rác. Hãy để nó làm công việc của mình.

+0

nói cách khác - không sử dụng finalize() để chơi các thủ đoạn trên bộ sưu tập –

0

Tôi tin rằng có một mẫu ở đó cho điều này. Không chắc chắn nếu nó là mẫu nhà máy. Nhưng bạn có một đối tượng tạo ra tất cả các đối tượng của bạn và giữ một tham chiếu đến chúng. Khi bạn đã kết thúc với họ, bạn de-tham khảo chúng trong nhà máy, làm cho cuộc gọi rõ ràng.

2

Tôi nghi ngờ những gì bạn có thể đề cập đến là nếu phương pháp finalize của bạn ngăn chặn một tham chiếu đến đối tượng đang được hoàn thành. Trong trường hợp này (nếu đọc của tôi về Java Language Spec là chính xác), phương pháp finalize sẽ không bao giờ được chạy lại, nhưng đối tượng sẽ chưa được thu gom rác.

Đây không phải là loại điều thực hiện trong cuộc sống thực, ngoại trừ có thể do tai nạn!

2

Điều này nghe có vẻ giống như một trong những câu hỏi phỏng vấn-chỉ-thời gian-bạn sẽ thấy. finalize() được chạy khi đối tượng của bạn đang thu thập rác, vì vậy nó sẽ là khá sai lầm để đặt một cái gì đó trong đó để ngăn chặn bộ sưu tập. Thông thường bạn chỉ cần giữ một tham chiếu và đó là tất cả những gì bạn cần.

Tôi thậm chí không chắc chắn điều gì sẽ xảy ra nếu bạn tạo một tham chiếu mới cho một cái gì đó trong finalizer - kể từ khi thu gom rác đã quyết định thu thập nó sau đó bạn sẽ kết thúc với một ref ref null? Có vẻ như một ý tưởng tồi tệ, trong mọi trường hợp. ví dụ.

public class Foo { 
    static Foo reference; 
    ... 
    finalize(){ 
    reference = this; 
    } 
} 

Tôi nghi ngờ điều này sẽ hoạt động hoặc có thể hoạt động nhưng phụ thuộc vào GC implenetation hoặc là "hành vi không xác định". Trông có vẻ xấu xa.

9

Bí quyết trả lời người phỏng vấn của bạn đang tìm kiếm có lẽ là anh ta muốn bạn biết rằng bạn có thể ngăn chặn việc thu gom rác từ việc loại bỏ một đối tượng bằng cách buộc rò rỉ bộ nhớ. Rõ ràng, nếu bạn giữ một tham chiếu đến đối tượng trong một số bối cảnh tồn tại lâu dài, nó sẽ không được thu thập, nhưng đó không phải là những gì nhà tuyển dụng của OP hỏi về nó. Đó không phải là điều xảy ra trong phương thức hoàn thiện.

Những gì bạn có thể làm để ngăn chặn thu gom rác thải từ bên trong phương thức Finalize là viết một vòng lặp vô hạn, trong đó bạn gọi Thread.yield(); (có lẽ là để giữ cho một vòng lặp trống từ được tối ưu hóa đi):

@Override 
protected void finalize() throws Throwable { 
    while (true) { 
     Thread.yield(); 
    } 
} 

My tham chiếu ở đây là một bài báo của Elliot Back, trong đó mô tả buộc rò rỉ bộ nhớ bằng phương pháp này.

Chỉ một cách khác trong đó phương thức hoàn tất là số.

+1

Tôi nghĩ rằng điểm không phải là "Thread.yield()" dừng bộ sưu tập rác, nhưng vòng lặp vô hạn đã làm - Thread.yield () đang ở đó để ngăn chặn vòng lặp vô hạn chiếm quá nhiều CPU. –

+0

@Score_Under Tốt bắt! Cảm ơn, tôi đã đọc lại bài viết và chỉnh sửa câu trả lời của tôi. Một lần nữa cám ơn. – CPerkins

1

Điểm mấu chốt là nếu chúng ta đặt biến tham chiếu thực trỏ đến đối tượng null, mặc dù chúng ta có các biến mẫu của lớp đó trỏ đến đối tượng đó không được đặt là rỗng. Đối tượng là tự động đủ điều kiện cho rác collection.if lưu các đối tượng để GC, sử dụng mã này ...

public class GcTest { 

    public int id; 
    public String name; 
    private static GcTest gcTest=null; 

    @Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 

     System.out.println("In finalize method."); 
     System.out.println("In finalize :ID :"+this.id); 
     System.out.println("In finalize :ID :"+this.name); 

     gcTest=this; 

    } 

    public static void main(String[] args) { 

     GcTest myGcTest=new GcTest(); 
     myGcTest.id=1001; 
     myGcTest.name="Praveen"; 
     myGcTest=null; 

     // requesting Garbage Collector to execute. 
     // internally GC uses Mark and Sweep algorithm to clear heap memory. 
     // gc() is a native method in RunTime class. 

     System.gc(); // or Runtime.getRuntime().gc(); 

     try { 
      Thread.sleep(2000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("\n------- After called GC() ---------\n"); 
     System.out.println("Id :"+gcTest.id); 
     System.out.println("Name :"+gcTest.name); 


    } 

} 

Output:

Trong phương pháp Finalize.
Trong Finalize: ID: 1001
Trong Finalize: ID: Praveen

------- Sau khi gọi GC() --------

Id: 1001
Tên : Praveen

3

Cách tốt nhất là sử dụng Unsafe, mặc dù ByteBuffer có thể là giải pháp khả thi cho một số trường hợp.

Cũng tìm kiếm từ khóa "bộ nhớ heap".

không an toàn

Ưu điểm qua ByteBuffer:

  • cho phép các đối tượng được biểu diễn trực tiếp, mà không cho serialization và do đó nhanh hơn
  • không kiểm tra giới hạn, vì vậy nhanh hơn
  • kiểm soát deallocation rõ ràng
  • có thể phân bổ nhiều hơn giới hạn JVM

Nó không phải là tuy nhiên dễ dàng để có được làm việc.Phương pháp này được mô tả trong bài viết sau đây:

Tất cả đều bao gồm các bước sau:

  • chúng ta cần một nhà điều hành sizeof mà Unsafe không có. Cách đặt một câu hỏi tại: In Java, what is the best way to determine the size of an object?. Các tùy chọn tốt nhất là khả năng các instrument API, nhưng điều đó đòi hỏi bạn phải tạo ra một Jar và sử dụng tùy chọn dòng lệnh đặc biệt ...

  • khi chúng tôi có sizeof, phân bổ đủ bộ nhớ với Unsafe#allocateMemory, mà cơ bản là một malloc và trả về một địa chỉ

  • tạo đối tượng heap trên thường xuyên, sao chép nó vào bộ nhớ được cấp phát với Unsafe#copyMemory. Để thực hiện điều này, bạn cần phải có địa chỉ của đối tượng trên heap và kích thước của đối tượng

  • đặt Object để trỏ đến bộ nhớ được phân bổ, sau đó truyền Object đến lớp học của bạn.

    Có vẻ như không thể đặt địa chỉ của biến trực tiếp với Không an toàn, vì vậy chúng tôi cần đưa đối tượng vào đối tượng mảng hoặc đối tượng bao bọc và sử dụng Unsafe#arrayBaseOffset hoặc Unsafe#objectFieldOffset.

  • một khi bạn đang thực hiện, giải phóng bộ nhớ được phân bổ với freeMemory

Nếu tôi đã từng được điều này để không segfault tôi sẽ đăng một ví dụ :-)

ByteBuffer

Ưu điểm vượt quá An toàn:

  • ổn định trên các phiên bản Java trong khi không an toàn có thể phá vỡ
  • không bị ràng buộc kiểm tra, vì vậy an toàn hơn ... không an toàn, cho phép rò rỉ bộ nhớ và SIGSEGV

JLS says:

Nội dung của bộ đệm trực tiếp có thể nằm bên ngoài đống rác thu thập thông thường.

Ví dụ về sử dụng với nguyên thủy:

ByteBuffer bb = ByteBuffer.allocateDirect(8); 

bb.putInt(0, 1); 
bb.putInt(4, 2); 
assert bb.getInt(0) == 1; 
assert bb.getInt(4) == 2; 

// Bound chekcs are done. 
boolean fail = false; 
try { 
    bb.getInt(8); 
} catch(IndexOutOfBoundsException e) { 
    fail = true; 
} 
assert fail; 

chủ đề liên quan:

1

Tôi tự hỏi nếu những gì họ sẽ cho là mô hình với các nhóm tài nguyên (ví dụ. cho các kết nối mạng/db hoặc chủ đề), nơi bạn sử dụng finalize để trả về tài nguyên cho nhóm để đối tượng thực sự đang nắm giữ tài nguyên không phải là GC'ed.

dụ Stupid, trong Java giống như mã giả và thiếu bất kỳ loại đồng bộ hóa:

class SlowResourceInternal { 
    private final SlowResourcePool parent; 
    <some instance data> 

    returnToPool() { 
     parent.add(this); 
    } 
} 

class SlowResourceHolder { 
    private final SlowResourceInternal impl; 

    <delegate actual stuff to the internal object> 

    finalize() { 
     if (impl != null) impl.returnToPool(); 
    } 
} 
Các vấn đề liên quan