2009-05-12 40 views
95

Tôi đang duyệt một số sách cũ và tìm thấy một bản sao của "Java thực hành" của Peter Hagger. Trong phần hiệu suất, có một đề xuất để đặt tham chiếu đối tượng thành null khi không còn cần thiết nữa.Không thiết lập các đối tượng Java để làm bất cứ điều gì nữa?

Trong Java, việc đặt tham chiếu đối tượng thành null có cải thiện hiệu suất thu thập hoặc hiệu suất thu gom rác không? Nếu vậy, trong trường hợp nào đây là một vấn đề? Các lớp container? Thành phần đối tượng? Các lớp bên trong vô danh?

Tôi thấy mã này khá thường xuyên. Đây có phải là lời khuyên lập trình lỗi thời hay nó vẫn hữu ích?

+2

Tiểu sử. Trên thời gian chạy hiện đại, bạn sẽ không thấy bất kỳ sự gia tăng đáng kể nào về hiệu suất hoặc dung lượng bộ nhớ. –

+12

@Jason, Tiểu sử? Điều đó giả định rằng tôi sẽ lập hồ sơ một bộ đủ lớn các trường hợp để có được một kết quả đủ tốt để trả lời câu hỏi này. Và tôi không chọn một bộ các trường hợp VM được tối ưu hóa đủ để che giấu các vấn đề về hiệu suất và gc. Đó là lý do tại sao tôi hỏi điều này ở đây. Để hiểu được các trường hợp mà đây là vấn đề. – sal

+1

Bản sao của http://stackoverflow.com/questions/449409/does-assigning-objects-to-null-in-java-impact-garbage-collection. –

Trả lời

65

Nó phụ thuộc một chút vào thời điểm bạn nghĩ đến việc hủy tham chiếu.

Nếu bạn có chuỗi đối tượng A-> B-> C, thì khi A không thể truy cập, A, B và C sẽ đủ điều kiện thu gom rác thải (giả sử không có gì khác liên quan đến B hoặc C). Không cần thiết, và không bao giờ có bất kỳ nhu cầu nào, để đặt rõ ràng các tham chiếu A-> B hoặc B-> C thành null, ví dụ.

Ngoài ra, phần lớn thời gian vấn đề không thực sự phát sinh, bởi vì trong thực tế bạn đang xử lý các đối tượng trong bộ sưu tập. Bạn nên thường xuyên nghĩ đến việc loại bỏ các đối tượng khỏi danh sách, bản đồ, vv bằng cách gọi phương thức remove().

Các trường hợp có từng là một số lời khuyên để thiết lập tài liệu tham khảo để null được cụ thể trong một phạm vi dài nơi một đối tượng nhiều bộ nhớ không còn được sử dụng partway qua phạm vi. Ví dụ:

{ 
    BigObject obj = ... 
    doSomethingWith(obj); 
    obj = null;    <-- explicitly set to null 
    doSomethingElse(); 
} 

Lý do ở đây là vì obj vẫn còn trong phạm vi, sau đó mà không xóa mỡ rõ ràng của tài liệu tham khảo, nó không trở thành rác thải thu hồi được cho đến sau khi phương pháp doSomethingElse() hoàn tất. Và đây là lời khuyên rằng có lẽ không còn giữ trên các JVM hiện đại: nó chỉ ra rằng trình biên dịch JIT có thể làm việc tại điểm mà một tham chiếu đối tượng cục bộ đã cho không còn được sử dụng nữa. lĩnh vực

+1

Biến địa phương có thể được tối ưu hóa bởi máy. Biến lớp không thể. Tôi đã thấy các luồng công nhân (trên một luồng thread) không giải phóng các đối tượng trạng thái của chúng sau khi xử lý yêu cầu và do đó ghim các đối tượng đó vào bộ nhớ cho đến khi chuỗi công việc được đưa ra một nhiệm vụ mới (ghi đè kịp thời các đối tượng trạng thái cũ với các đối tượng mới). Với các đối tượng trạng thái lớn và hàng trăm luồng công nhân (nghĩ rằng: máy chủ http lớn), điều này có thể là một lượng lớn bộ nhớ được giữ và sao chép xung quanh nhưng sẽ không bao giờ được sử dụng nữa. –

+0

Tôi có lẽ chỉ nên thêm: lời khuyên tôi đưa ra ở trên là lời khuyên để theo dõi chung, giả sử các thư viện được xử lý tốt, một máy ảo được xử lý tốt. Nếu bạn thấy thư viện hồ bơi của bạn bị treo vào đối tượng sau khi một công việc đã hoàn thành, tốt ... đó là một lỗi trong thư viện thread thread đã nói có thể được làm việc xung quanh bằng cách nulling một tham chiếu đối tượng, nhưng cũng có thể được làm việc xung quanh bằng cách sử dụng một thư viện hồ bơi ít lỗi. Tôi không chắc rằng một lỗi như vậy thay đổi nguyên tắc thiết kế chung. –

24

Không, đó không phải là lời khuyên lỗi thời. Dangling tài liệu tham khảo vẫn còn là một vấn đề, đặc biệt là nếu bạn đang, nói, thực hiện một container mở rộng mảng (ArrayList hoặc tương tự) bằng cách sử dụng một mảng được phân bổ trước. Các yếu tố vượt quá kích thước "hợp lý" của danh sách phải được vô hiệu hóa, nếu không chúng sẽ không được giải phóng.

Xem Hiệu quả phiên bản 2 của Java, Mục 6: Loại bỏ tham chiếu đối tượng lỗi thời.

+0

Đây có phải là sự cố ngôn ngữ hoặc sự cố triển khai VM không? –

+4

Đó là vấn đề "ngữ nghĩa". Về cơ bản, bởi vì bạn đã preallocated một mảng, VM thấy điều đó.Nó không biết gì về kích thước "hợp lý" của vùng chứa của bạn. Giả sử bạn có một ArrayList có kích thước 10, được hỗ trợ bởi mảng 16 phần tử. VM không thể biết rằng các mục 10..15 không thực sự được sử dụng; nếu những khe đó có nội dung, chúng sẽ không được giải phóng. –

+0

Còn bên ngoài lớp học của container thì sao? Trong thành phần đối tượng nơi đối tượng bên trong không được phân bổ bởi đối tượng bên ngoài. – sal

4

Trong môi trường hạn chế bộ nhớ (ví dụ: điện thoại di động), điều này có thể hữu ích. Bằng cách thiết lập null, objetc không cần phải đợi biến để thoát khỏi phạm vi để được gc'd.

Đối với chương trình hàng ngày, tuy nhiên, đây không phải là quy tắc, ngoại trừ trong các trường hợp đặc biệt như trường hợp Chris Jester-Young đã trích dẫn.

9

Instance, phần tử mảng

Nếu có một tham chiếu đến một đối tượng, nó có thể không được thu gom rác thải. Đặc biệt là nếu đối tượng đó (và toàn bộ đồ thị phía sau nó) lớn, chỉ có một tham chiếu dừng thu gom rác, và tham chiếu đó không thực sự cần thiết nữa, đó là một tình huống không may.

Trường hợp bệnh lý là đối tượng duy trì một thể hiện đơn nhất đối với toàn bộ cây DOM XML được sử dụng để định cấu hình nó, MBean chưa được đăng ký hoặc tham chiếu đơn lẻ đến đối tượng từ ứng dụng web chưa được xử lý ngăn chặn toàn bộ trình nạp lớp không bị tải xuống.

Vì vậy, trừ khi bạn chắc chắn rằng đối tượng giữ tham chiếu chính nó sẽ được thu thập rác anyway (hoặc thậm chí sau đó), bạn nên null ra tất cả mọi thứ mà bạn không còn cần.

biến Scoped:

Nếu bạn đang xem xét thiết lập một biến địa phương để null trước khi kết thúc phạm vi của nó, vì vậy nó có thể được khai hoang bởi thu gom rác và để đánh dấu nó như là "không sử dụng được từ bây giờ ", bạn nên xem xét đặt nó trong một phạm vi hạn chế hơn để thay thế.

{ 
    BigObject obj = ... 
    doSomethingWith(obj); 
    obj = null;   // <-- explicitly set to null 
    doSomethingElse(); 
} 

trở thành

{ 
    { 
    BigObject obj = ... 
    doSomethingWith(obj); 
    } //   <-- obj goes out of scope 
    doSomethingElse(); 
} 

Long, phạm vi phẳng nói chung là xấu đối với mức độ dễ đọc của mã, quá. Giới thiệu các phương pháp riêng để phá vỡ mọi thứ chỉ vì mục đích đó không phải là chưa từng nghe thấy.

+0

Nếu bạn đang đề cập đến một tài liệu tham khảo mạnh mẽ, có điều này là đúng, nhưng không phải cho tất cả các tài liệu tham khảo. Tham chiếu yếu trong java có thể là rác được thu thập. –

0

Thứ nhất, nó không có nghĩa là bất cứ điều gì bạn đang thiết lập một đối tượng để null. Tôi giải thích nó bên dưới:

List list1=new ArrayList(); 
List list2=list1; 

Trong đoạn mã trên, chúng ta đang tạo danh sách tên biến thể tham chiếu 1 đối tượng ArrayList được lưu trữ trong bộ nhớ. Vì vậy, list1 đang đề cập đến đối tượng đó và nó ghi nhận nhiều hơn một biến. Và trong dòng thứ hai của mã chúng tôi đang sao chép tham chiếu của list1 vào danh sách2. Vì vậy bây giờ sẽ trở lại câu hỏi của bạn nếu tôi làm:

list1=null; 

đó có nghĩa là list1 không còn ám chỉ bất kỳ đối tượng được lưu trữ trong bộ nhớ nên List2 cũng sẽ có gì để tham khảo. Vì vậy, nếu bạn kiểm tra kích thước của List2:

list2.size(); //it gives you 0 

Vì vậy, đây là khái niệm về thu gom rác đến mà nói "bạn không có gì phải lo lắng về giải phóng bộ nhớ đó là giữ bởi các đối tượng, tôi sẽ làm điều đó khi tôi thấy rằng nó sẽ không còn được sử dụng trong chương trình và JVM sẽ quản lý tôi. "

Tôi hy vọng nó sẽ rõ ràng khái niệm.

0

Một trong những lý do để làm như vậy là loại bỏ các tham chiếu đối tượng lỗi thời. Bạn có thể đọc văn bản here.

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