Việc ghi đè Object.finalize () có thực sự xấu không?


34

Hai đối số chính chống lại ghi đè Object.finalize()là:

  1. Bạn không được quyết định khi nào nó được gọi.

  2. Nó có thể không được gọi ở tất cả.

Nếu tôi hiểu điều này một cách chính xác, tôi không nghĩ đó là những lý do đủ tốt để ghét Object.finalize()quá nhiều.

  1. Tùy thuộc vào việc triển khai VM và GC để xác định thời điểm thích hợp để phân bổ một đối tượng, không phải là nhà phát triển. Tại sao điều quan trọng là quyết định khi Object.finalize()được gọi?

  2. Thông thường, và sửa lỗi cho tôi nếu tôi sai, lần duy nhất Object.finalize()sẽ không được gọi là khi ứng dụng bị chấm dứt trước khi GC có cơ hội chạy. Tuy nhiên, dù sao thì đối tượng đã bị xử lý khi quá trình của ứng dụng kết thúc với nó. Vì vậy, Object.finalize()đã không được gọi vì không cần phải gọi. Tại sao các nhà phát triển quan tâm?

Mỗi lần tôi sử dụng các đối tượng mà tôi phải đóng thủ công (như xử lý tệp và kết nối), tôi rất bực mình. Tôi phải liên tục kiểm tra xem một đối tượng có triển khai close()hay không và tôi chắc chắn rằng tôi đã bỏ lỡ một vài cuộc gọi đến nó tại một số điểm trong quá khứ. Tại sao nó không đơn giản và an toàn hơn khi chỉ để nó cho VM và GC xử lý các đối tượng này bằng cách đưa việc close()triển khai vào Object.finalize()?


1
Cũng lưu ý: giống như nhiều API từ thời Java 1.0, ngữ nghĩa của luồng finalize()hơi bị rối. Nếu bạn từng thực hiện nó, hãy đảm bảo rằng nó an toàn cho tất cả các phương thức khác trên cùng một đối tượng.
billc.cn

2
Khi bạn nghe người ta nói rằng trình hoàn thiện là xấu, điều đó không có nghĩa là chương trình của bạn sẽ ngừng hoạt động nếu bạn có chúng; họ có nghĩa là toàn bộ ý tưởng hoàn thiện là khá vô dụng.
dùng253751

1
+1 cho câu hỏi này. Hầu hết các câu trả lời dưới đây nói rằng các tài nguyên như mô tả tệp bị giới hạn và chúng phải được thu thập bằng tay. Điều tương tự cũng đúng với bộ nhớ, vì vậy nếu chúng tôi chấp nhận một số độ trễ trong bộ sưu tập bộ nhớ, tại sao không chấp nhận nó cho mô tả tệp và / hoặc các tài nguyên khác?
mbonnin

Giải quyết đoạn cuối cùng của bạn, bạn có thể để nó vào Java để xử lý các việc đóng như xử lý tệp và kết nối với rất ít nỗ lực từ phía bạn. Sử dụng khối tài nguyên dùng thử - nó đã được đề cập một vài lần khác trong câu trả lời và nhận xét, nhưng tôi nghĩ rằng nó đáng để đặt ở đây. Hướng dẫn của Oracle cho việc này được tìm thấy tại docs.oracle.com/javase/tutorial/essential/exceptions/ mẹo
Jeutnarg

Câu trả lời:


45

Theo kinh nghiệm của tôi, có một và chỉ một lý do để ghi đè Object.finalize(), nhưng đó là một lý do rất chính đáng :

Để đặt mã đăng nhập lỗi trong finalize()đó thông báo cho bạn nếu bạn quên gọi close().

Phân tích tĩnh chỉ có thể bắt gặp thiếu sót trong các tình huống sử dụng tầm thường và các cảnh báo của trình biên dịch được đề cập trong câu trả lời khác có một cái nhìn đơn giản như vậy về những điều mà bạn thực sự phải vô hiệu hóa chúng để thực hiện mọi thứ không tầm thường. (Tôi có nhiều cảnh báo được kích hoạt hơn bất kỳ lập trình viên nào khác mà tôi biết hoặc đã từng nghe nói, nhưng tôi không bật cảnh báo ngu ngốc.)

Hoàn thiện dường như là một cơ chế tốt để đảm bảo rằng các tài nguyên không bị ảnh hưởng, nhưng hầu hết mọi người nhìn nhận nó theo một cách hoàn toàn sai: họ nghĩ về nó như một cơ chế dự phòng thay thế, một "cơ hội thứ hai" sẽ tự động cứu ngày bằng cách xử lý các tài nguyên mà họ quên. Đây là sai lầm chết người . Chỉ có một cách để thực hiện bất kỳ điều gì đã cho: hoặc bạn luôn đóng mọi thứ hoặc hoàn thành luôn đóng mọi thứ. Nhưng vì quyết toán là không đáng tin cậy, quyết toán có thể là nó.

Vì vậy, có sơ đồ này mà tôi gọi là Xử lý bắt buộc và nó quy định rằng lập trình viên có trách nhiệm luôn luôn đóng một cách rõ ràng mọi thứ thực hiện Closeablehoặc AutoCloseable. (Câu lệnh try-with-resource vẫn được tính là đóng rõ ràng.) Tất nhiên, lập trình viên có thể quên, vì vậy đó là nơi hoàn thành trò chơi, nhưng không phải là một nàng tiên ma thuật cuối cùng sẽ làm mọi thứ trở nên kỳ diệu: Nếu cuối cùng phát hiện ra Điều đó close()không được viện dẫn, nó khôngcố gắng gọi nó, chính xác bởi vì sẽ có (với sự chắc chắn về mặt toán học) sẽ có một nhóm lập trình viên n00b, những người sẽ dựa vào nó để làm công việc mà họ quá lười biếng hoặc quá lơ đãng để làm. Vì vậy, với việc xử lý bắt buộc, khi hoàn tất phát hiện ra mà close()không được gọi, nó sẽ ghi một thông báo lỗi màu đỏ sáng, nói với lập trình viên bằng các chữ cái viết hoa lớn để sửa lỗi của mình.

Như một lợi ích bổ sung, có tin đồn rằng "JVM sẽ bỏ qua một phương thức quyết định () không quan trọng (ví dụ như phương thức trả về mà không làm gì cả, như phương thức được định nghĩa trong lớp Object)", vì vậy, với việc xử lý bắt buộc, bạn có thể tránh tất cả quyết toán chi phí trong toàn bộ hệ thống của bạn ( xem câu trả lời của alip để biết thông tin về mức chi phí khủng khiếp này) bằng cách mã hóa finalize()phương pháp của bạn như thế này:

@Override
protected void finalize() throws Throwable
{
    if( Global.DEBUG && !closed )
    {
        Log.Error( "FORGOT TO CLOSE THIS!" );
    }
    //super.finalize(); see alip's comment on why this should not be invoked.
}

Ý tưởng đằng sau đó Global.DEBUGlà một static finalbiến có giá trị được biết đến tại thời điểm biên dịch, vì vậy nếu đó là falsetrình biên dịch sẽ không phát ra bất kỳ mã nào cho toàn bộ ifcâu lệnh, điều này sẽ biến nó thành một bộ hoàn thiện tầm thường (trống) có nghĩa là lớp của bạn sẽ được đối xử như thể nó không có bộ hoàn thiện. (Trong C #, điều này sẽ được thực hiện với một #if DEBUGkhối đẹp , nhưng chúng ta có thể làm gì, đây là java, nơi chúng ta trả tiền đơn giản rõ ràng trong mã với chi phí bổ sung trong não.)

Tìm hiểu thêm về Xử lý bắt buộc, với thảo luận bổ sung về việc xử lý tài nguyên trong dot Net, tại đây: michael.gr: Xử lý bắt buộc so với việc ghê tởm "vứt bỏ"


2
@MikeNakis Đừng quên, Closable được định nghĩa là không làm gì nếu được gọi lần thứ hai: docs.oracle.com/javase/7/docs/api/java/io/Closizable.html . Tôi thừa nhận rằng đôi khi tôi đã ghi lại một cảnh báo khi các lớp học của tôi bị đóng hai lần, nhưng về mặt kỹ thuật, bạn thậm chí không cần phải làm điều đó. Về mặt kỹ thuật, việc gọi .close () trên Closable nhiều lần là hoàn toàn hợp lệ.
Patrick M

1
@usr tất cả tập trung vào việc bạn tin tưởng vào thử nghiệm của mình hay bạn không tin tưởng vào thử nghiệm của mình. Nếu bạn không tin tưởng vào thử nghiệm của mình, chắc chắn, hãy tiếp tục và chịu chi phí quyết toán cũng vậy close() , chỉ trong trường hợp. Tôi tin rằng nếu thử nghiệm của tôi không được tin cậy, thì tốt nhất tôi không nên phát hành hệ thống để sản xuất.
Mike Nakis

3
@Mike, để if( Global.DEBUG && ...cấu trúc hoạt động, do đó JVM sẽ bỏ qua finalize()phương thức này là tầm thường, Global.DEBUGphải được đặt ở thời gian biên dịch (trái ngược với nội dung được chèn, v.v.) vì vậy phần tiếp theo sẽ là mã chết. Lệnh gọi super.finalize()bên ngoài khối if cũng đủ để JVM coi nó là không tầm thường (bất kể giá trị của Global.DEBUG, ít nhất là trên HotSpot 1.8) ngay cả khi siêu hạng đó #finalize()là tầm thường!
alip

1
@Mike Tôi sợ đó là trường hợp. Tôi đã thử nghiệm nó với (một phiên bản sửa đổi một chút) của bài kiểm tra trong bài viết mà bạn đã liên kết và đầu ra GC dài dòng (cùng với hiệu suất kém đáng ngạc nhiên) xác nhận rằng các đối tượng được sao chép vào không gian thế hệ cũ / sống sót và cần có đầy đủ heap để có được loại bỏ.
alip

1
Điều thường bị bỏ qua, là nguy cơ thu thập các đối tượng sớm hơn dự kiến, khiến việc giải phóng tài nguyên trong bộ hoàn thiện là một hành động rất nguy hiểm. Trước Java 9, cách duy nhất để chắc chắn rằng bộ hoàn thiện không đóng tài nguyên trong khi nó vẫn đang được sử dụng, là đồng bộ hóa trên đối tượng, trong cả hai, bộ hoàn thiện và (các) phương thức sử dụng tài nguyên. Đó là lý do tại sao nó hoạt động java.io. Nếu đó là loại an toàn thread là không phải trên danh sách mong muốn, nó làm tăng thêm chi phí gây ra bởi finalize()...
Holger

28

Mỗi lần tôi sử dụng các đối tượng mà tôi phải đóng thủ công (như xử lý tệp và kết nối), tôi rất bực mình. [...] Tại sao không đơn giản và an toàn hơn khi chỉ để nó cho VM và GC xử lý các đối tượng này bằng cách đưa việc close()triển khai vào Object.finalize()?

Bởi vì các kết nối và xử lý tệp (nghĩa là các mô tả tệp trên các hệ thống Linux & POSIX) là một nguồn tài nguyên khan hiếm (bạn có thể bị giới hạn ở 256 trong số chúng trên một số hệ thống hoặc ở 16384 trên một số hệ thống khác; xem setrlimit (2) ). Không có gì đảm bảo rằng GC sẽ được gọi đủ thường xuyên (hoặc vào đúng thời điểm) để tránh làm cạn kiệt các nguồn lực hạn chế như vậy. Và nếu GC không được gọi là đủ (hoặc quyết toán không được chạy đúng lúc), bạn sẽ đạt đến giới hạn (có thể thấp).

Hoàn thiện là một điều "nỗ lực tốt nhất" trong JVM. Nó có thể không được gọi hoặc được gọi là khá muộn ... Đặc biệt, nếu bạn có nhiều RAM hoặc nếu chương trình của bạn không phân bổ nhiều đối tượng (hoặc nếu hầu hết chúng chết trước khi được chuyển tiếp đến một số đủ cũ được tạo bởi một thế hệ sao chép), GC có thể được gọi là khá hiếm, và các quyết toán có thể không chạy thường xuyên (hoặc thậm chí có thể không chạy).

Vì vậy, closemô tả tập tin rõ ràng, nếu có thể. Nếu bạn sợ rò rỉ chúng, hãy sử dụng quyết toán như một biện pháp bổ sung, không phải là biện pháp chính.


7
Tôi sẽ thêm rằng việc đóng một luồng vào một tệp hoặc ổ cắm thường làm sạch nó. Để các luồng mở không cần thiết làm tăng nguy cơ mất dữ liệu nếu mất điện, mất kết nối (đây cũng là rủi ro đối với các tệp được truy cập qua mạng), v.v.

2
Trên thực tế, mặc dù số lượng bộ lọc cho phép có thể thấp, nhưng đó không phải là điểm thực sự khó khăn, bởi vì ít nhất người ta có thể sử dụng nó như một tín hiệu cho GC. Điều thực sự có vấn đề đó là a) hoàn toàn không liên quan đến GC có bao nhiêu tài nguyên không do GC quản lý treo trên đó và b) nhiều tài nguyên đó là duy nhất, vì vậy những tài nguyên khác sẽ bị chặn hoặc bị từ chối.
Ded repeatator

2
Và nếu bạn để một tệp mở, nó có thể gây trở ngại cho người khác sử dụng nó. (Người xem Windows 8 XPS, tôi đang nhìn bạn!)
Loren Pechtel

2
"Nếu bạn sợ rò rỉ [mô tả tệp], hãy sử dụng quyết toán như một biện pháp bổ sung, chứ không phải là biện pháp chính." Câu nói này nghe có vẻ tanh với tôi. Nếu bạn thiết kế mã của mình tốt, bạn có thực sự nên giới thiệu mã dự phòng lan truyền dọn dẹp trên nhiều nơi không?
mucaho

2
@BasileStarynkevitch Quan điểm của bạn là trong một thế giới lý tưởng, vâng, sự dư thừa là xấu, nhưng trong thực tế, nơi bạn không thể thấy trước tất cả các khía cạnh liên quan, tốt hơn là an toàn hơn xin lỗi?
mucaho

13

Hãy xem xét vấn đề theo cách này: bạn chỉ nên viết mã là (a) đúng (nếu không chương trình của bạn sai) và (b) cần thiết (nếu không mã của bạn quá lớn, có nghĩa là cần nhiều RAM hơn, cần nhiều chu kỳ hơn những thứ vô dụng, nhiều nỗ lực để hiểu nó hơn, dành nhiều thời gian hơn cho việc duy trì nó, v.v.)

Bây giờ hãy xem xét những gì bạn muốn thực hiện trong một quyết toán. Hoặc là nó cần thiết. Trong trường hợp đó, bạn không thể đặt nó vào bộ hoàn thiện, vì bạn không biết liệu nó có được gọi không. Đó chưa đủ tốt. Hoặc nó không cần thiết - sau đó bạn không nên viết nó ở nơi đầu tiên! Dù bằng cách nào, đưa nó vào hoàn thiện là lựa chọn sai lầm.

(Lưu ý rằng các ví dụ bạn đặt tên, như đóng luồng tệp, trông như thể chúng không thực sự cần thiết, nhưng chúng là như vậy cho đến khi bạn đạt giới hạn xử lý tệp mở trên hệ thống của mình, bạn sẽ không nhận thấy rằng code đang không chính xác. Nhưng giới hạn đó là một tính năng của hệ điều hành và do đó thậm chí còn không thể đoán trước hơn chính sách của JVM cho finalizers, vì vậy nó thực sự, thực sự quan trọng mà bạn không lãng phí xử lý tập tin.)


Nếu tôi chỉ có nghĩa vụ viết mã là "cần thiết" thì tôi có nên tránh tất cả kiểu dáng trong tất cả các ứng dụng GUI của mình không? Chắc chắn không có gì là cần thiết; GUI sẽ hoạt động tốt mà không cần kiểu dáng, chúng sẽ trông khủng khiếp. Về bộ hoàn thiện, có thể cần phải làm một cái gì đó, nhưng vẫn ổn để đặt nó vào bộ hoàn thiện, vì bộ hoàn thiện sẽ được gọi trong đối tượng GC, nếu có. Nếu bạn cần đóng tài nguyên, cụ thể là khi nó sẵn sàng để thu gom rác, thì bộ hoàn thiện là tối ưu. Chương trình chấm dứt giải phóng tài nguyên của bạn hoặc trình hoàn thiện được gọi.
Kröw

Nếu tôi có RAM nặng, vật thể đắt tiền, có thể đóng và tôi quyết định tham chiếu nó yếu, để có thể xóa nó khi không cần thiết, thì tôi có thể sử dụng finalize()để đóng nó trong trường hợp chu kỳ gc thực sự cần giải phóng RAM. Mặt khác, tôi sẽ giữ đối tượng trong RAM thay vì phải tạo lại và đóng nó mỗi khi tôi cần sử dụng nó. Chắc chắn, tài nguyên mà nó mở sẽ không được giải phóng cho đến khi đối tượng được cấp phép, bất cứ khi nào có thể, nhưng tôi có thể không cần đảm bảo rằng tài nguyên của mình sẽ được giải phóng vào một thời điểm nhất định.
Kröw

8

Một trong những lý do lớn nhất để không phụ thuộc vào người hoàn thiện là hầu hết các tài nguyên mà người ta có thể bị cám dỗ để làm sạch trong công cụ hoàn thiện là rất hạn chế. Trình thu gom rác chỉ chạy thường xuyên, vì đi qua các tham chiếu để xác định xem có thể phát hành thứ gì đó đắt tiền hay không. Điều này có nghĩa là nó có thể 'mất một lúc' trước khi các đối tượng của bạn thực sự bị phá hủy. Ví dụ, nếu bạn có nhiều đối tượng mở các kết nối cơ sở dữ liệu ngắn hạn, việc để các bộ hoàn thiện để dọn sạch các kết nối này có thể làm cạn kiệt kết nối của bạn trong khi nó chờ trình thu gom rác cuối cùng chạy và giải phóng các kết nối đã hoàn thành. Sau đó, vì chờ đợi, bạn kết thúc với một lượng lớn các yêu cầu xếp hàng, nhanh chóng làm cạn kiệt kết nối lại. Nó '

Ngoài ra, việc sử dụng tài nguyên thử với việc đóng các đối tượng 'có thể đóng' khi hoàn thành dễ dàng thực hiện. Nếu bạn không quen thuộc với cấu trúc này, tôi khuyên bạn nên kiểm tra nó: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html


8

Ngoài lý do tại sao để nó cho bộ hoàn thiện để giải phóng tài nguyên nói chung là một ý tưởng tồi, các đối tượng có thể hoàn thiện đi kèm với chi phí hoạt động.

Từ lý thuyết và thực hành Java: Thu thập và hiệu suất rác (Brian Goetz), bộ hoàn thiện không phải là bạn của bạn :

Các đối tượng có bộ hoàn thiện (những đối tượng có phương thức quyết toán () không tầm thường) có chi phí đáng kể so với các đối tượng không có bộ hoàn thiện và nên được sử dụng một cách tiết kiệm. Các đối tượng cuối cùng là cả chậm để phân bổ và chậm hơn để thu thập. Tại thời điểm phân bổ, JVM phải đăng ký bất kỳ đối tượng có thể hoàn thiện nào với trình thu gom rác và (ít nhất là trong triển khai JVM của HotSpot) phải theo một đường dẫn phân bổ chậm hơn hầu hết các đối tượng khác. Tương tự, các đối tượng cuối cùng cũng chậm để thu thập. Phải mất ít nhất hai chu kỳ thu gom rác (trong trường hợp tốt nhất) trước khi có thể thu hồi một đối tượng có thể hoàn thành và người thu gom rác phải làm thêm để gọi trình hoàn thiện. Kết quả là dành nhiều thời gian hơn để phân bổ và thu thập các đối tượng và áp lực nhiều hơn đối với người thu gom rác, bởi vì bộ nhớ được sử dụng bởi các đối tượng cuối cùng không thể truy cập được giữ lại lâu hơn. Kết hợp với thực tế là các công cụ hoàn thiện không được đảm bảo để chạy trong bất kỳ khung thời gian dự đoán nào, hoặc thậm chí, và bạn có thể thấy rằng có khá ít tình huống sử dụng công cụ phù hợp.


Điểm tuyệt vời. Xem câu trả lời của tôi cung cấp một phương tiện để tránh chi phí hoạt động finalize().
Mike Nakis

7

Lý do ưa thích (ít nhất) của tôi để tránh Object.finalizekhông phải là các đối tượng có thể được hoàn thiện sau khi bạn mong đợi, nhưng chúng có thể được hoàn thiện trước khi bạn có thể mong đợi. Vấn đề không phải là một đối tượng vẫn còn trong phạm vi có thể được hoàn thành trước khi thoát khỏi phạm vi nếu Java quyết định nó không còn có thể truy cập được nữa.

void test() {
   HasFinalize myObject = ...;
   OutputStream os = myObject.stream;

   // myObject is no-longer reachable at this point, 
   // even though it is in scope. But objects are finalized
   // based on reachability.
   // And since finalization is on another thread, it 
   // could happen before or in the middle of the write .. 
   // closing the stream and causing much fun.
   os.write("Hello World");
}

Xem câu hỏi này để biết thêm chi tiết. Điều thú vị hơn nữa là quyết định này chỉ có thể được đưa ra sau khi tối ưu hóa điểm nóng, khiến điều này trở nên khó khăn để gỡ lỗi.


1
Điều đó là HasFinalize.streambản thân nó nên là một đối tượng hoàn thiện riêng biệt. Đó là, quyết toán HasFinalizekhông nên hoàn thiện hoặc cố gắng dọn dẹp stream. Hoặc nếu cần, thì nó sẽ streamkhông thể truy cập được.
tài năng


4

Tôi phải liên tục kiểm tra xem một đối tượng có triển khai close () không và tôi chắc chắn rằng tôi đã bỏ lỡ một vài cuộc gọi đến nó tại một số điểm trong quá khứ.

Trong Eclipse, tôi nhận được một cảnh báo bất cứ khi nào tôi quên đóng một cái gì đó thực hiện Closeable / AutoCloseable. Tôi không chắc đó là một thứ Eclipse hay nếu nó là một phần của trình biên dịch chính thức, nhưng bạn có thể xem xét sử dụng các công cụ phân tích tĩnh tương tự để giúp bạn ở đó. Ví dụ, FindBugs có thể giúp bạn kiểm tra xem bạn đã quên đóng tài nguyên chưa.


1
Ý kiến ​​hay đề cập đến AutoCloseable. Nó làm cho nó dễ dàng để quản lý tài nguyên với các tài nguyên thử. Điều này vô hiệu hóa một số đối số trong câu hỏi.

2

Cho câu hỏi đầu tiên của bạn:

Tùy thuộc vào việc triển khai VM và GC để xác định thời điểm thích hợp để phân bổ một đối tượng, không phải là nhà phát triển. Tại sao điều quan trọng là quyết định khi Object.finalize()được gọi?

Chà, JVM sẽ xác định, khi đó là một điểm tốt để lấy lại bộ lưu trữ đã được phân bổ cho một đối tượng. Đây không nhất thiết là thời điểm mà tài nguyên dọn sạch, bạn muốn thực hiện finalize(), nên xảy ra. Điều này được minh họa trong phần hoàn thiện () được gọi là đối tượng có thể tiếp cận mạnh mẽ trong câu hỏi Java 8 trên Java . Ở đó, một close()phương thức đã được gọi bởi mộtfinalize() phương thức, trong khi nỗ lực đọc từ luồng bởi cùng một đối tượng vẫn đang chờ xử lý. Vì vậy, bên cạnh khả năng nổi tiếng finalize()bị gọi là muộn, có khả năng nó được gọi quá sớm.

Tiền đề của câu hỏi thứ hai của bạn:

Thông thường, và sửa tôi nếu tôi sai, lần duy nhất Object.finalize() sẽ không được gọi là khi ứng dụng bị chấm dứt trước khi GC có cơ hội chạy.

đơn giản là sai. Không có yêu cầu cho một JVM để hỗ trợ hoàn thiện cả. Chà, điều đó không hoàn toàn sai vì bạn vẫn có thể diễn giải nó vì ứng dụng đã bị chấm dứt trước khi hoàn tất, diễn ra, giả định rằng ứng dụng của bạn sẽ chấm dứt.

Nhưng hãy lưu ý sự khác biệt nhỏ giữa Tuyên bố ban đầu của bạn và thuật ngữ quyết định ban đầu của bạn. Thu gom rác là khác biệt để hoàn thiện. Khi quản lý bộ nhớ phát hiện ra rằng một đối tượng không thể truy cập được, nó có thể chỉ cần lấy lại không gian của nó, nếu không, nó không có một finalize()phương thức đặc biệt hoặc hoàn thiện đơn giản là không được hỗ trợ, hoặc nó có thể thu hút đối tượng để hoàn thiện. Do đó, việc hoàn thành một chu trình thu gom rác không có nghĩa là các công cụ hoàn thiện được thực thi. Điều đó có thể xảy ra sau đó, khi hàng đợi được xử lý, hoặc không bao giờ.

Điểm này cũng là lý do tại sao ngay cả trên các JVM có hỗ trợ hoàn thiện, việc dựa vào nó để làm sạch tài nguyên là nguy hiểm. Bộ sưu tập rác là một phần của quản lý bộ nhớ và do đó được kích hoạt bởi nhu cầu bộ nhớ. Có thể bộ sưu tập rác không bao giờ chạy vì có đủ bộ nhớ trong toàn bộ thời gian chạy (tốt, nó vẫn phù hợp với ứng dụng đã bị chấm dứt trước khi GC có cơ hội chạy mô tả, bằng cách nào đó). Cũng có thể là GC chạy nhưng sau đó, có đủ bộ nhớ được thu hồi, do đó hàng đợi bộ hoàn thiện không được xử lý.

Nói cách khác, tài nguyên bản địa được quản lý theo cách này vẫn còn xa lạ đối với việc quản lý bộ nhớ. Mặc dù được đảm bảo rằng một OutOfMemoryErrorchỉ được ném sau khi đủ nỗ lực để giải phóng bộ nhớ, nhưng điều đó không áp dụng cho tài nguyên bản địa và quyết toán. Có thể việc mở tệp không thành công do không đủ tài nguyên trong khi hàng đợi quyết toán có đầy đủ các đối tượng có thể giải phóng các tài nguyên này, nếu trình hoàn thiện đã từng chạ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.