2013-08-02 75 views
13

Gần đây tôi đã tìm kiếm giải phóng bộ nhớ bị chiếm bởi các đối tượng Java. Trong khi làm điều đó tôi đã nhầm lẫn về cách đối tượng được sao chép (nông/sâu) trong Java và làm thế nào để tránh vô tình xóa/vô hiệu hóa các đối tượng trong khi chúng vẫn đang được sử dụng.xoá hoặc đặt null thành đối tượng trong java

Hãy xem xét kịch bản sau đây:

  1. đi qua một ArrayList<Object> như một cuộc tranh cãi đến một phương pháp.
  2. chuyển một số ArrayList<Object> đến một lớp có thể chạy được để xử lý theo chuỗi.
  3. đặt ArrayList<Object> vào HashMap.

Trong trường hợp này, nếu tôi gọi list = null; hoặc list.clear();, điều gì sẽ xảy ra với đối tượng? Trong trường hợp đó các đối tượng bị mất và trong trường hợp nào chỉ tham chiếu được đặt thành null?

Tôi đoán nó phải làm với việc sao chép nông và sâu của các đối tượng, nhưng trong trường hợp nào sao chép cạn xảy ra và trong trường hợp nào bản sao sâu xảy ra trong Java?

+1

tôi luôn luôn tìm thấy một trong những khía cạnh hấp dẫn nhất của Java là thu gom rác ... –

Trả lời

36

Thứ nhất, bạn không bao giờ đặt đối tượng thành không. Khái niệm đó không có ý nghĩa. Bạn có thể chỉ định giá trị null cho một biến số , nhưng bạn cần phân biệt giữa các khái niệm "biến" và "đối tượng" rất cẩn thận. Khi bạn thực hiện, câu hỏi của bạn sẽ tự trả lời chính xác :)

Hiện tại về "bản sao nông" và "bản sao sâu" - có thể đáng để tránh cụm từ "bản sao nông" ở đây một đối tượng mới, nhưng chỉ cần sao chép trực tiếp các trường của đối tượng hiện có. Một bản sao sâu cũng sẽ lấy một bản sao của các đối tượng được các trường đó nhắc đến (đối với các trường kiểu tham chiếu). Một nhiệm vụ đơn giản như thế này:

ArrayList<String> list1 = new ArrayList<String>(); 
ArrayList<String> list2 = list1; 

... không làm hoặc một bản sao cạn hoặc một bản sao sâu trong ý nghĩa đó. Nó chỉ sao chép tham chiếu. Sau mã ở trên, list1list2 là các biến độc lập - chúng chỉ xảy ra có cùng giá trị (tham chiếu) tại thời điểm này. Chúng ta có thể thay đổi giá trị của một trong số họ, và nó sẽ không ảnh hưởng đến người khác:

list1 = null; 
System.out.println(list2.size()); // Just prints 0 

Bây giờ nếu thay vì thay đổi biến, chúng tôi thực hiện thay đổi đối tượng mà giá trị của các biến tham khảo , sự thay đổi đó sẽ được hiển thị thông qua biến khác quá:

list2.add("Foo"); 
System.out.println(list1.get(0)); // Prints Foo 

Vì vậy, trở lại câu hỏi ban đầu của bạn - bạn không bao giờ lưu trữ đối tượng thực tế trong một bản đồ, danh sách, mảng vv bạn chỉ bao giờ lưu trữ tài liệu tham khảo .Một đối tượng chỉ có thể được thu thập rác khi không có cách nào để "sống" mã tiếp cận đối tượng đó nữa. Vì vậy, trong trường hợp này:

List<String> list = new ArrayList<String>(); 
Map<String, List<String>> map = new HashMap<String, List<String>>(); 
map.put("Foo", list); 
list = null; 

... đối tượng ArrayList vẫn có thể không được thu gom rác thải, bởi vì Map có một mục trong đó đề cập đến nó.

+0

thnks cho việc xóa nó không liên quan gì đến việc sao chép cạn hoặc sâu !! – anzaan

1

Nó phụ thuộc vào có bao nhiêu biến được referenciating cho mỗi đối tượng của bạn, để giải thích này sẽ tốt hơn một số mã:

Object myAwesomeObject = new Object(); 
List<Object> myList = new ArrayList<Object>(); 

myList.add(myAwesomeObject); 

myList = null; // Your object hasn't been claimed by the GC just yet, your variable "myAwesomeObject" is still refering to it 

myAwesomeObject = null; // done, now your object is eligible for garbage collection. 

Vì vậy, nó không phụ thuộc việc bạn vượt qua ArrayList của bạn như một đối số một phương thức hoặc tương tự, nó phụ thuộc vào việc có bao nhiêu biến vẫn tham chiếu đến các đối tượng của bạn.

+0

Bạn quên: 'myList.add (myAwesomeObject); ':))) – alfasin

+0

DUH !!! cảm ơn @alfasin – morgano

1

Nếu bạn đặt danh sách vào bản đồ băm, bản đồ băm hiện giữ một tham chiếu đến danh sách.

Nếu bạn chuyển danh sách làm đối số cho phương thức, phương pháp sẽ có tham chiếu đến phương pháp đó trong suốt thời gian của phương pháp.

Nếu bạn chuyển nó vào một chuỗi để thao tác, chuỗi sẽ có tham chiếu đến đối tượng cho đến khi nó kết thúc.

Trong tất cả các trường hợp này, nếu bạn đặt list = null, các tham chiếu sẽ vẫn được duy trì, nhưng chúng sẽ biến mất sau khi các tham chiếu này biến mất.

Nếu bạn chỉ xóa danh sách, tham chiếu sẽ vẫn hợp lệ, nhưng bây giờ sẽ trỏ đến danh sách đột nhiên bị dọn sạch, có nghĩa là người lập trình có thể không biết và có thể bị coi là lỗi, sử dụng chủ đề.

1

Java GC tự động xác nhận quyền sở hữu đối tượng khi chúng không được tham chiếu ở bất kỳ đâu. Vì vậy, trong hầu hết các trường hợp, bạn sẽ phải đặt tham chiếu là null rõ ràng

Ngay khi phạm vi biến kết thúc, đối tượng sẽ đủ điều kiện cho GC và được giải phóng nếu không có điểm tham chiếu nào khác đối tượng.

Java là chuyển giá trị vì vậy nếu bạn đặt danh sách là null trong phương pháp thì nó sẽ không ảnh hưởng đến tham chiếu ban đầu được chuyển cho bạn trong phương thức.

public class A{ 

    private List<Integer> list = new ArrayList<Integer>(); 
    public static void main(String[] args) { 
     A a = new A(); 
     B b = new B(); 

     b.method(a.list); 

     System.out.println(a.list.size()); //Will print 0 and not throw NullPointerException 
    } 

} 

class B{ 
    public void method(List<Integer> list){ 
     list = null; 
     //just this reference is set to null and not the original one 
     //so list of A will not be GCed 
    } 
} 
1

Nếu bạn chuyển một ArrayList vào một phương thức thì list = null sẽ không có hiệu lực nếu có tham chiếu trực tiếp đến danh sách ở đâu đó, ví dụ như trong mã gọi. Nếu bạn gọi list.clear() ở bất kỳ nơi nào trong mã, tham chiếu đến các đối tượng từ danh sách này sẽ bị vô hiệu. Việc chuyển tham chiếu đến phương thức không phải là bản sao nông, nó đang chuyển tham chiếu theo giá trị

0

Gần đây tôi đã tìm kiếm giải phóng bộ nhớ bị chiếm bởi các đối tượng java.

Một lời khuyên.

Thường là một ý tưởng tồi để suy nghĩ về điều này. Và nó thường là một ý tưởng tồi tệ hơn để cố gắng "giúp đỡ". Trong 99,8% trường hợp, trình thu gom rác của Java có thể thực hiện công việc thu gom rác thải tốt hơn nếu bạn thực sự để nó tiếp tục với nó ... và đừng lãng phí công sức của bạn bằng cách gán null cho mọi thứ. Thật vậy, các cơ hội là các lĩnh vực bạn đang nulling là trong các đối tượng đó là về để trở thành unreachable anyway. Và trong trường hợp đó, GC thậm chí sẽ không xem xét các trường mà bạn đã bỏ trống.

Nếu bạn thực hiện quan điểm này (thực dụng), tất cả suy nghĩ của bạn về các bản sao nông so với chiều sâu và khi nó an toàn cho những thứ rỗng là tranh luận.


Có một tỷ lệ nhỏ các trường hợp, nơi nó được khuyến khích để gán null ... để tránh rò rỉ lưu trữ trung hạn hoặc dài. Và nếu bạn đang ở trong một trong những tình huống hiếm hoi mà nó là "tái chế" đối tượng thực sự là một ý tưởng tốt, sau đó nulling cũng được khuyến khích.

4

Để xóa biến

Theo hiểu biết của tôi,

Nếu bạn đang đi để tái sử dụng các biến, sau đó sử dụng

   Object.clear(); 

Nếu bạn sẽ không để tái sử dụng , sau đó xác định

   Object=null; 

Lưu ý: So sánh với removeAll(), clear() nhanh hơn.

Xin vui lòng sửa tôi, Nếu tôi sai ....