Việc đặt các đối tượng Java thành null có còn tác dụng gì không?


112

Tôi đang xem một số cuốn sách cũ và tìm thấy một bản sao của "Thực hành Java" của Peter Hagger. Trong phần hiệu suất, có một khuyến nghị để đặt tham chiếu đối tượng thành nullkhi không còn cần thiết.

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

Tôi thấy điều này trong mã khá thường xuyên. Lời khuyên lập trình này hiện đã lỗi thời hay nó vẫn hữu ích?


2
Hồ sơ nó. Trên các thời gian chạy hiện đại, bạn sẽ không thấy bất kỳ sự gia tăng có ý nghĩa nào về hiệu suất hoặc diện tích bộ nhớ.
Jason Coco

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

Câu trả lời:


73

Nó phụ thuộc một chút vào thời điểm bạn nghĩ đến việc vô hiệu hóa 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 được, A, B và C đều sẽ đủ điều kiện để thu gom rác (giả sử không có gì khác đề cập đến B hoặc C). Chẳng hạn, không cần và chưa bao giờ cần đặt các tham chiếu A-> B hoặc B-> C thành null.

Ngoài ra, hầu hết các trường hợp 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. Nói chung, bạn nên luôn nghĩ đến việc xóa các đối tượng khỏi danh sách, bản đồ, v.v. bằng cách gọi phương thức remove () phê duyệt.

Trường hợp đã từng có một số lời khuyên để đặt tham chiếu đến null cụ thể là trong một phạm vi dài, nơi một đối tượng sử dụng nhiều bộ nhớ không còn được sử dụng nữa trong 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, do đó nếu không có tham chiếu vô hiệu rõ ràng, nó sẽ không trở thành có thể thu gom rác cho đến khi phương thức doSomethingElse () hoàn thành. Và đây là lời khuyên mà có lẽ không còn phù hợp với các JVM hiện đại : hóa ra trình biên dịch JIT có thể hoạt động tại thờ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.


2
Các biến cục bộ có thể được máy tối ưu hóa. Các biến lớp không thể. Tôi đã thấy các luồng công nhân (trên một nhóm luồng) 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 đó trong bộ nhớ cho đến khi luồng công nhân được giao một tác vụ mới (nhanh chóng ghi đè các đối tượng trạng thái cũ bằng 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 (hãy nghĩ: máy chủ http lớn), đây có thể là một lượng lớn bộ nhớ được lưu giữ và sao chép xung quanh nhưng sẽ không bao giờ được sử dụng nữa.
Brian White

1
Tôi có lẽ chỉ nên nói thêm: lời khuyên tôi đưa ra ở trên là lời khuyên nên làm theo nói chung, giả sử các thư viện hoạt động tốt, một máy ảo hoạt động tốt, v.v. Nếu bạn thấy rằng bạn có, chẳng hạn như một thư viện nhóm luồng gắn vào các đối tượng sau một công việc đã hoàn thành, tốt ... đó là một lỗi trong thư viện nhóm luồng có thể được khắc phục bằng cách vô hiệu hóa một tham chiếu đối tượng, nhưng cũng có thể được giải quyết bằng cách sử dụng thư viện nhóm luồng ít lỗi hơn. Tôi không chắc rằng một lỗi như vậy sẽ thay đổi các nguyên tắc thiết kế chung.
Neil Coffey

25

Không, đó không phải là lời khuyên lỗi thời. Tham chiếu nguy hiểm vẫn là một vấn đề, đặc biệt nếu bạn đang triển khai một vùng chứa mảng có thể mở rộng ( ArrayListhoặc tương tự) bằng cách sử dụng một mảng được phân bổ trước. Các phần tử vượt quá kích thước "hợp lý" của danh sách nên được xóa, nếu không chúng sẽ không được giải phóng.

Xem Hiệu quả Java xuất bản lần thứ 2, Mục 6: Loại bỏ các tham chiếu đối tượng lỗi thời.


Đây là vấn đề ngôn ngữ hay vấn đề triển khai VM?
Pablo Santa Cruz

4
Đó là một vấn đề "ngữ nghĩa". Về cơ bản, vì bạn đã định vị trước một mảng, máy ảo nhận 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ó ArrayList có kích thước 10, được hỗ trợ bởi một mảng 16 phần tử. Máy ảo 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.
Chris Jester-Young

những gì về bên ngoài của các lớp container? Trong thành phần đối tượng, nơi đối tượng bên trong không được đối tượng bên ngoài phân bổ.
sal

7
@sal Nguyên tắc chung là nếu bạn không thể tham chiếu nó, nó sẽ được thu gom. Vì vậy, nếu đối tượng bên ngoài chứa một tham chiếu đến một đối tượng khác, giả sử rằng đối tượng bên trong không có bất kỳ tham chiếu nào khác, việc đặt tham chiếu một và chỉ của đối tượng bên ngoài thành null sẽ khiến toàn bộ đối tượng bị thu thập, bao gồm cả các tham chiếu mồ côi của nó.
ubermensch

2
Trong cùng một Mục 6 mà bạn đã đề cập: Việc xóa bỏ các tham chiếu đối tượng nên là ngoại lệ chứ không phải là chuẩn.
ACV

10

Trường phiên bản, phần tử mảng

Nếu có một tham chiếu đến một đối tượng, nó không thể được thu thập rác. Đặc biệt nếu đối tượng đó (và toàn bộ đồ thị phía sau nó) lớn, chỉ có một tham chiếu đang 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 đáng tiếc.

Các trường hợp bệnh lý là đối tượng giữ lại một phiên bản không cần thiết cho 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 duy nhất đến một đối tượng từ một ứng dụng web chưa được triển khai ngăn không cho tải toàn bộ bộ nạp lớp .

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

Các biến phạm vi:

Nếu bạn đang cân nhắc việc đặt một biến cục bộ thành null trước khi kết thúc phạm vi của nó, để bộ thu gom rác có thể thu hồi và đánh dấu nó là "không thể sử dụng từ bây giờ trở đi", bạn nên cân nhắc đặt nó vào một phạm vi hạn chế hơn. .

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

trở thành

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

Phạm vi dài, phẳng nói chung cũng không tốt cho tính dễ đọc của mã. Việc giới thiệu các phương pháp riêng tư để phá vỡ mọi thứ chỉ vì mục đích đó không phải là không có.


Nếu bạn đang đề cập đến một tham chiếu mạnh, thì điều này đúng, nhưng không phải cho tất cả các tham chiếu. Các tham chiếu yếu trong java có thể được thu thập rác.
James Drinkard,

5

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

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


1

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

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

Trong đoạn mã trên, chúng ta đang tạo tên biến tham chiếu đối tượng list1của ArrayListđối tượng được lưu trong bộ nhớ. Vì vậy, list1tham chiếu đến đối tượng đó và nó không hơn gì một biến. Và trong dòng mã thứ hai, chúng tôi đang sao chép tham chiếu list1đến list2. Vì vậy, bây giờ trở lại câu hỏi của bạn nếu tôi làm:

list1 = null;

điều đó có nghĩa list1là không còn tham chiếu đến bất kỳ đối tượng nào được lưu trữ trong bộ nhớ vì vậy list2cũng sẽ không có gì để tham chiếu. 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 khái niệm bộ thu gom rác xuất hiện nói rằng «bạn không cần lo lắng về việc giải phóng bộ nhớ được giữ bởi đố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ó 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 ở đâ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.