Có một hàm hủy cho Java không?


594

Có một hàm hủy cho Java không? Tôi dường như không thể tìm thấy bất kỳ tài liệu về điều này. Nếu không, làm thế nào tôi có thể đạt được hiệu quả tương tự?

Để làm cho câu hỏi của tôi cụ thể hơn, tôi đang viết một ứng dụng liên quan đến dữ liệu và thông số kỹ thuật nói rằng cần có nút 'đặt lại' để đưa ứng dụng trở lại trạng thái ban đầu. Tuy nhiên, tất cả dữ liệu phải được 'sống' trừ khi đóng ứng dụng hoặc nhấn nút đặt lại.

Thường là một lập trình viên C / C ++, tôi nghĩ rằng việc này sẽ không quan trọng để thực hiện. (Và do đó tôi đã lên kế hoạch thực hiện nó lần cuối.) Tôi đã cấu trúc chương trình của mình sao cho tất cả các đối tượng 'có thể đặt lại' sẽ nằm trong cùng một lớp để tôi có thể phá hủy tất cả các đối tượng 'sống' khi nhấn nút đặt lại.

Tôi đã suy nghĩ nếu tất cả những gì tôi làm chỉ là để hủy bỏ dữ liệu và chờ người thu gom rác thu thập chúng, liệu có bị rò rỉ bộ nhớ nếu người dùng của tôi liên tục nhập dữ liệu và nhấn nút đặt lại không? Tôi cũng đã suy nghĩ vì Java khá trưởng thành như một ngôn ngữ, nên có một cách để ngăn chặn điều này xảy ra hoặc xử lý một cách duyên dáng điều này.


7
Chỉ có rò rỉ bộ nhớ nếu bạn giữ các tham chiếu đến các đối tượng bạn không cần. tức là có một lỗi trong chương trình của bạn GC sẽ chạy khi cần (đôi khi sớm hơn)
Peter Lawrey

17
Máy ảo sẽ không chạy GC sớm nếu bạn đang xử lý nhanh dữ liệu thông qua các đối tượng. Ý tưởng rằng GC luôn có thể theo kịp, hoặc đưa ra quyết định đúng đắn là sai lầm.
Kieveli

1
@Kieveli JVM sẽ không chạy GC trước khi báo lỗi?
WVrock

4
Vâng, thật tuyệt nếu có công cụ hủy diệt cho Java sẽ phá hủy nó một lần cho tất cả.
Tomáš Zato - Phục hồi Monica

Câu trả lời:


526

Bởi vì Java là ngôn ngữ được thu thập rác, bạn không thể dự đoán khi nào (hoặc thậm chí nếu) một đối tượng sẽ bị hủy. Do đó không có tương đương trực tiếp của một hàm hủy.

Có một phương thức được kế thừa được gọi finalize, nhưng điều này được gọi hoàn toàn theo quyết định của người thu gom rác. Vì vậy, đối với các lớp cần dọn dẹp rõ ràng, quy ước là xác định một phương thức đóng và chỉ sử dụng hoàn thiện để kiểm tra độ tỉnh táo (tức là nếu đóng chưa được gọi hãy thực hiện ngay bây giờ và ghi lại lỗi).

một câu hỏi xuất hiện thảo luận chuyên sâu về hoàn thiện gần đây, vì vậy sẽ cung cấp thêm chiều sâu nếu được yêu cầu ...


5
Có phải "close ()" trong ngữ cảnh này đề cập đến phương thức trong java.lang.Autoclosizable không?
Sridhar Sarnobat

21
Không, AutoClosizable đã được giới thiệu trong Java 7 nhưng quy ước 'close ()' đã tồn tại lâu hơn nhiều.
Jon Onstott

tại sao bạn không thể dự đoán khi nào (hoặc thậm chí nếu) một đối tượng sẽ bị phá hủy. những gì gần đúng cách khác để dự đoán đó?
dctremblay

Việc phá hủy đối tượng @dctremblay được thực hiện bởi trình thu gom rác và trình thu gom rác có thể không bao giờ chạy trong vòng đời của ứng dụng.
Piro nói Phục hồi Monica

4
Lưu ý rằng finalizephương thức này không được dùng nữa trong Java 9.
Lii

124

Có một cái nhìn vào tuyên bố thử với tài nguyên . Ví dụ:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  System.out.println(br.readLine());
} catch (Exception e) {
  ...
} finally {
  ...
}

Ở đây, tài nguyên không còn cần thiết được giải phóng trong BufferedReader.close()phương thức. Bạn có thể tạo lớp riêng của mình thực hiện AutoCloseablevà sử dụng nó theo cách tương tự.

Tuyên bố này hạn chế hơn finalizevề mặt cấu trúc mã, nhưng đồng thời nó làm cho mã đơn giản hơn để hiểu và duy trì. Ngoài ra, không có gì đảm bảo rằng một finalizephương thức được gọi trong suốt thời gian tồn tại của ứng dụng.


12
Tôi ngạc nhiên vì điều này có rất ít phiếu bầu. Đó là câu trả lời thực tế.
Nurettin

20
Tôi không đồng ý rằng đó là câu trả lời thực tế. Nếu một cá thể có một tài nguyên mà nó xử lý trong một khoảng thời gian lớn hơn, qua nhiều cuộc gọi phương thức, thì việc thử với các tài nguyên sẽ không có ích. Trừ khi bạn có thể đóng và mở lại tài nguyên theo tỷ lệ nói rằng các phương thức được gọi tại - không phải là một thực tế chung.
Eric

14
Thật vậy, đây không phải là câu trả lời thực tế. Không thể sử dụng cấu trúc này để quản lý việc phá hủy một đối tượng trừ khi việc xây dựng và sử dụng của đối tượng được gói gọn hoàn toàn bởi tryfinallyđược sử dụng để buộc một cuộc gọi đến obj.finalize(). Và ngay cả thiết lập này cũng không giải quyết được vấn đề do OP: phá hủy đối tượng giữa chương trình được kích hoạt bằng nút "đặt lại".
7yl4r

1
Những người dùng khác đã cho thấy điều này được thực hiện trong điểm nhập ứng dụng của bạn. Xác định biến của bạn trên toàn cầu. Khởi tạo nó trong chức năng nhập cảnh, với thử. Khử lại cuối cùng (khi ứng dụng của bạn đóng). Nó là hoàn toàn có thể.
TamusJRoyce

1
@nurettin Java 7 chỉ mới ra mắt được 3 tháng khi câu hỏi được hỏi, nếu điều đó giúp hiểu ý nghĩa của nó nhiều hơn.
corsiKa

110

Không, không có kẻ hủy diệt ở đây. Lý do là tất cả các đối tượng Java được phân bổ đống và rác được thu thập. Không có sự giải quyết rõ ràng (tức là toán tử xóa của C ++), không có cách nào hợp lý để thực hiện các hàm hủy thực sự.

Java không hỗ trợ các công cụ hoàn thiện, nhưng chúng chỉ được sử dụng như một biện pháp bảo vệ cho các đối tượng nắm giữ các tài nguyên bản địa như socket, xử lý tệp, xử lý cửa sổ, v.v. Khi trình thu gom rác thu thập một đối tượng mà không cần bộ hoàn thiện, nó chỉ đơn giản đánh dấu bộ nhớ khu vực là miễn phí và đó là nó. Khi đối tượng có bộ hoàn thiện, đầu tiên nó được sao chép vào một vị trí tạm thời (hãy nhớ rằng chúng tôi đang thu gom rác ở đây), sau đó nó được đưa vào hàng đợi chờ được hoàn thiện và sau đó một chuỗi Trình hoàn thiện sẽ thăm dò hàng đợi với mức độ ưu tiên rất thấp và chạy bộ hoàn thiện.

Khi ứng dụng thoát, JVM dừng lại mà không đợi các đối tượng đang chờ xử lý được hoàn thành, do đó, thực tế không có gì đảm bảo rằng trình hoàn thiện của bạn sẽ chạy.


4
Cảm ơn bạn đã đề cập đến tài nguyên bản địa - đây là một lĩnh vực mà phương pháp "giống như kẻ hủy diệt" hữu ích.
Nathan Osman

vâng, tôi đang đối mặt với vấn đề tương tự ngay bây giờ với việc giải phóng tài nguyên / xử lý được phân bổ thông qua các cuộc gọi riêng đến C ++.
nikk

@ddimitrov, trên lý thuyết java có thể thực hiện thỏa thuận rõ ràng không? Hay đây là một mâu thuẫn logic?
mils

1
@mils ngây thơ thực hiện thỏa thuận rõ ràng sẽ phá vỡ giả định Java rằng bất kỳ điểm tham chiếu nào đến một đối tượng sống. Bạn có thể lặp lại trên tất cả các con trỏ và loại bỏ các bí danh, nhưng điều đó đắt hơn so với GC. Hoặc bạn có thể thử sử dụng một số hệ thống loại tuyến tính (xem "quyền sở hữu" trong Rust), nhưng đó là một thay đổi ngôn ngữ lớn. Cũng có các tùy chọn khác (xem bộ nhớ trong phạm vi JavaRT, v.v.), nhưng nói chung, việc phân bổ rõ ràng không phù hợp với ngôn ngữ Java.
ddimitrov

31

Nên tránh sử dụng các phương thức Finalize () . Chúng không phải là một cơ chế đáng tin cậy để làm sạch tài nguyên và có thể gây ra sự cố trong trình thu gom rác bằng cách lạm dụng chúng.

Nếu bạn yêu cầu một cuộc gọi thỏa thuận trong đối tượng của mình, hãy nói để giải phóng tài nguyên, sử dụng một cuộc gọi phương thức rõ ràng. Quy ước này có thể được nhìn thấy trong các API hiện có (ví dụ: Closable , Graphics.dispose () , Widget.dispose () ) và thường được gọi thông qua try / cuối cùng.

Resource r = new Resource();
try {
    //work
} finally {
    r.dispose();
}

Nỗ lực sử dụng một đối tượng bị loại bỏ sẽ ném ngoại lệ thời gian chạy (xem IllegalStateException ).


BIÊN TẬP:

Tôi đã suy nghĩ, nếu tất cả những gì tôi làm chỉ là để hủy bỏ dữ liệu và chờ người thu gom rác thu thập, liệu có bị rò rỉ bộ nhớ nếu người dùng của tôi liên tục nhập dữ liệu và nhấn nút đặt lại không?

Nói chung, tất cả những gì bạn cần làm là hủy bỏ các đối tượng - ít nhất, đây là cách nó được cho là hoạt động. Nếu bạn lo lắng về việc thu gom rác, hãy xem Điều chỉnh bộ sưu tập rác máy ảo Java SE 6 (tm] (hoặc tài liệu tương đương cho phiên bản JVM của bạn).


1
Đó không phải là ý nghĩa của sự trung thành. Đó không phải là "đặt tham chiếu cuối cùng của một đối tượng thành null" mà là nhận (đọc) giá trị từ một tham chiếu để bạn có thể sử dụng nó cho các hoạt động tiếp theo.

1
Là thử..tất nhiên vẫn là một cách tiếp cận hợp lệ và được đề nghị? Giả sử, trước đây tôi đã gọi một phương thức gốc trong Finalize (), tôi có thể di chuyển cuộc gọi đến mệnh đề cuối cùng không? class Resource { finalize() { destroy(); } protected native void destroy(); } class Alt_Resource { try (Resource r = new Resource()) { // use r } finalize { r.destroy(); }
aashima

r sẽ không được xếp vào khối cuối cùng. Do đó, bạn không thể gọi phá hủy tại thời điểm đó. Bây giờ, nếu bạn sửa phạm vi để tạo đối tượng trước khối thử, bạn sẽ kết thúc với trường hợp "trước khi thử với tài nguyên" xấu xí.
userAsh

21

Với Java 1.7 được phát hành, giờ đây bạn có thêm tùy chọn sử dụng try-with-resourceskhối. Ví dụ,

public class Closeable implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("closing..."); 
    }
    public static void main(String[] args) {
        try (Closeable c = new Closeable()) {
            System.out.println("trying..."); 
            throw new Exception("throwing..."); 
        }
        catch (Exception e) {
            System.out.println("catching..."); 
        }
        finally {
            System.out.println("finalizing..."); 
        } 
    }
}

Nếu bạn thực hiện lớp này, c.close()sẽ được thực thi khi trykhối còn lại và trước khi khối catchfinallyđược thực thi. Không giống như trong trường hợp của finalize()phương thức, close()được đảm bảo được thực thi. Tuy nhiên, không cần thực hiện nó một cách rõ ràng trong finallymệnh đề.


Điều gì xảy ra nếu chúng ta không sử dụng khối tài nguyên dùng thử? Tôi nghĩ rằng chúng ta có thể gọi close in Finalize () chỉ để đảm bảo rằng close đã được gọi.
shintoZ

3
@shintoZ như tôi đã đọc trong các câu trả lời ở trên, không có gì đảm bảo cho finalize()việc thực hiện
Asif Mushtaq

14

Tôi hoàn toàn đồng ý với các câu trả lời khác, nói rằng đừng dựa vào việc thực hiện quyết toán.

Ngoài các khối try-Catch-cuối cùng, bạn có thể sử dụng Runtime # addShutdownHook (được giới thiệu trong Java 1.3) để thực hiện các lần dọn dẹp cuối cùng trong chương trình của bạn.

Điều đó không giống như các hàm hủy , nhưng người ta có thể thực hiện một hook hook có các đối tượng người nghe đã đăng ký theo đó các phương thức dọn dẹp (đóng các kết nối cơ sở dữ liệu liên tục, loại bỏ các khóa tệp, v.v.) có thể được gọi - những điều thường được thực hiện trong chất phá hủy . Một lần nữa - đây không phải là sự thay thế cho các hàm hủy nhưng trong một số trường hợp, bạn có thể tiếp cận chức năng mong muốn với điều này.

Ưu điểm của việc này là có hành vi giải cấu trúc được ghép lỏng lẻo từ phần còn lại của chương trình.


addShutdownHook rõ ràng đã được giới thiệu trong Java 1.3. Dù sao, nó có sẵn cho tôi trong 1.5. :) Xem này: stackoverflow.com/questions/727151/ từ
Skiphoppy

1
FYI, theo kinh nghiệm của tôi, các móc tắt máy sẽ không được gọi nếu bạn sử dụng nút "chấm dứt" màu đỏ trong Eclipse - toàn bộ JVM bị hủy ngay lập tức, các móc tắt máy không được gọi một cách duyên dáng. Có nghĩa là bạn có thể thấy các hành vi khác nhau trong quá trình phát triển và sản xuất nếu bạn phát triển bằng cách sử dụng nhật thực
Hamy

11

Không, java.lang.Object#finalizelà gần nhất bạn có thể nhận được.

Tuy nhiên, khi (và nếu) nó được gọi, không được bảo đảm.
Xem:java.lang.Runtime#runFinalizersOnExit(boolean)


5
Một phương pháp có thể hoặc không thể được gọi về cơ bản là vô dụng trong cuốn sách của tôi. Sẽ tốt hơn nếu không làm ô nhiễm ngôn ngữ bằng một phương pháp đặc biệt vô dụng mà tốt nhất là mang lại cảm giác an toàn sai lầm. Tôi sẽ không bao giờ hiểu tại sao các nhà phát triển ngôn ngữ Java nghĩ rằng hoàn thiện là một ý tưởng tốt.
antred

@antred Các nhà phát triển ngôn ngữ Java đồng ý . Tôi đoán, hồi đó, đối với một số người trong số họ, đây là lần đầu tiên họ thiết kế ngôn ngữ lập trình và môi trường thời gian chạy với bộ sưu tập rác. Điều ít hiểu hơn, là tại sao ngôn ngữ được quản lý khác sao chép khái niệm đó tại một thời điểm, khi nó đã được hiểu rằng khái niệm này là một ý tưởng tồi.
Holger

7

Đầu tiên, lưu ý rằng vì Java là rác được thu thập, nên hiếm khi cần phải làm bất cứ điều gì về phá hủy đối tượng. Thứ nhất bởi vì bạn thường không có bất kỳ tài nguyên được quản lý nào miễn phí và thứ hai là vì bạn không thể dự đoán khi nào hoặc nếu nó sẽ xảy ra, do đó, không phù hợp với những thứ bạn cần xảy ra "ngay khi không còn ai sử dụng đối tượng của tôi nữa ".

Bạn có thể được thông báo sau khi một đối tượng đã bị hủy bằng cách sử dụng java.lang.ref.Ph PhantomReference (thực tế, nói rằng nó đã bị phá hủy có thể không chính xác một chút, nhưng nếu một tham chiếu ảo đến nó được xếp hàng thì nó không thể phục hồi được nữa, thường là điều tương tự). Một cách sử dụng phổ biến là:

  • Tách các tài nguyên trong lớp của bạn cần được hủy vào một đối tượng trợ giúp khác (lưu ý rằng nếu tất cả những gì bạn đang làm là đóng một kết nối, đó là trường hợp phổ biến, bạn không cần phải viết một lớp mới: kết nối được đóng sẽ là "đối tượng trợ giúp" trong trường hợp đó).
  • Khi bạn tạo đối tượng chính của mình, hãy tạo PhantomReference cho nó. Điều này có liên quan đến đối tượng trợ giúp mới hoặc thiết lập bản đồ từ các đối tượng PhantomReference đến các đối tượng trợ giúp tương ứng của chúng.
  • Sau khi đối tượng chính được thu thập, PhantomReference được xếp hàng (hoặc đúng hơn là nó có thể được xếp hàng - giống như các bộ hoàn thiện, không có gì đảm bảo nó sẽ tồn tại, ví dụ nếu VM thoát thì nó sẽ không chờ). Hãy chắc chắn rằng bạn đang xử lý hàng đợi của nó (theo một chủ đề đặc biệt hoặc theo thời gian). Do tham chiếu cứng đến đối tượng trợ giúp, đối tượng trợ giúp vẫn chưa được thu thập. Vì vậy, hãy làm bất cứ việc gì bạn muốn trên đối tượng trợ giúp, sau đó loại bỏ PhantomReference và người trợ giúp cuối cùng cũng sẽ được thu thập.

Ngoài ra còn có Final (), trông giống như một hàm hủy nhưng không hoạt động như một. Nó thường không phải là một lựa chọn tốt.


Tại sao một PhantomReference thay vì WeakReference?
uckelman

2
@uckelman: nếu tất cả những gì bạn muốn là thông báo, thì PhantomReference thực hiện công việc, đây là khá nhiều những gì nó được thiết kế cho. Các ngữ nghĩa bổ sung của WeakReference không cần thiết ở đây và tại thời điểm mà ReferenceQueue của bạn được thông báo, bạn không còn có thể khôi phục đối tượng thông qua WeakReference, vì vậy lý do duy nhất để sử dụng nó là để lưu nhớ rằng PhantomReference tồn tại. Bất kỳ công việc làm thêm nào mà WeakReference thực hiện có lẽ không đáng kể, nhưng tại sao phải bận tâm với nó?
Steve Jessop

Cảm ơn bạn đã gợi ý tại PhantomReference. Nó không hoàn hảo, nhưng vẫn tốt hơn không có gì.
foo

@SteveJessop, bạn nghĩ gì về công việc làm thêm của Google, bạn có tham chiếu yếu so với tham chiếu ảo không?
Holger

6

Các finalize()chức năng là destructor.

Tuy nhiên, nó không nên được sử dụng bình thường bởi vì nó được gọi sau GC và bạn không thể biết khi nào điều đó sẽ xảy ra (nếu có).

Hơn nữa, phải mất nhiều hơn một GC để phân bổ các đối tượng có finalize().

Bạn nên cố gắng dọn sạch ở những vị trí hợp lý trong mã của bạn bằng cách sử dụng các try{...} finally{...}câu lệnh!


5

Tôi đồng ý với hầu hết các câu trả lời.

Bạn không nên phụ thuộc hoàn toàn vào một trong hai finalizehoặcShutdownHook

hoàn thiện

  1. JVM không đảm bảo khi nào finalize()phương thức này sẽ được gọi.

  2. finalize()chỉ được gọi một lần bởi luồng GC. Nếu một đối tượng tự hồi sinh từ phương thức hoàn thiện, thì finalizesẽ không được gọi lại.

  3. Trong ứng dụng của bạn, bạn có thể có một số đối tượng sống, trên đó bộ sưu tập rác không bao giờ được gọi.

  4. Bất kỳ Exceptioncái nào được ném bởi phương thức hoàn thiện đều bị bỏ qua bởi luồng GC

  5. System.runFinalization(true)Runtime.getRuntime().runFinalization(true)các phương thức làm tăng xác suất của finalize()phương thức gọi nhưng bây giờ hai phương thức này đã không được dùng nữa. Các phương pháp này rất nguy hiểm do thiếu an toàn luồng và có thể tạo bế tắc.

tắt máy

public void addShutdownHook(Thread hook)

Đăng ký một móc tắt máy ảo mới.

Máy ảo Java tắt để đáp ứng với hai loại sự kiện:

  1. Chương trình thoát bình thường, khi luồng không phải daemon cuối cùng thoát hoặc khi phương thức exit (tương đương System.exit) được gọi, hoặc
  2. Máy ảo bị chấm dứt để đáp ứng với sự gián đoạn của người dùng, chẳng hạn như gõ ^ C hoặc một sự kiện toàn hệ thống, chẳng hạn như đăng xuất người dùng hoặc tắt hệ thống.
  3. Một hook shutdown chỉ đơn giản là một luồng khởi tạo nhưng không bắt đầu. Khi máy ảo bắt đầu trình tự tắt máy, nó sẽ bắt đầu tất cả các móc tắt đã đăng ký theo một số thứ tự không xác định và để chúng chạy đồng thời. Khi tất cả các hook đã hoàn thành, nó sẽ chạy tất cả các bộ hoàn thiện chưa được kích hoạt nếu tính năng hoàn tất khi thoát đã được bật.
  4. Cuối cùng, máy ảo sẽ dừng lại. Lưu ý rằng các luồng daemon sẽ tiếp tục chạy trong chuỗi tắt máy, cũng như các luồng không daemon nếu tắt máy được bắt đầu bằng cách gọi phương thức thoát.
  5. Móc tắt máy cũng nên hoàn thành công việc của họ một cách nhanh chóng. Khi một chương trình gọi thoát, kỳ vọng là máy ảo sẽ nhanh chóng tắt và thoát.

    Nhưng ngay cả tài liệu của Oracle cũng trích dẫn rằng

Trong một số trường hợp hiếm hoi, máy ảo có thể hủy bỏ, nghĩa là ngừng chạy mà không tắt một cách sạch sẽ

Điều này xảy ra khi máy ảo bị chấm dứt bên ngoài, ví dụ như với SIGKILLtín hiệu trên Unix hoặc TerminateProcesscuộc gọi trên Microsoft Windows. Máy ảo cũng có thể hủy bỏ nếu một phương thức gốc bị lỗi, ví dụ, làm hỏng cấu trúc dữ liệu nội bộ hoặc cố gắng truy cập bộ nhớ không tồn tại. Nếu máy ảo hủy bỏ thì không có đảm bảo nào có thể được thực hiện về việc liệu có bất kỳ móc tắt máy nào sẽ được chạy hay không.

Kết luận : sử dụng try{} catch{} finally{}các khối một cách thích hợp và giải phóng các tài nguyên quan trọng trong finally(}khối. Trong quá trình phát hành tài nguyên trong finally{}khối, bắt ExceptionThrowable.


4

Nếu đó chỉ là ký ức mà bạn lo lắng, thì đừng. Chỉ cần tin tưởng vào GC nó làm một công việc tốt. Tôi thực sự đã thấy một cái gì đó về nó hiệu quả đến mức có thể tốt hơn cho hiệu suất để tạo ra vô số vật thể nhỏ hơn là sử dụng các mảng lớn trong một số trường hợp.


3

Có lẽ bạn có thể sử dụng một khối thử ... cuối cùng để hoàn thiện đối tượng trong luồng điều khiển mà bạn đang sử dụng đối tượng. Tất nhiên nó không tự động xảy ra, nhưng cũng không phá hủy trong C ++. Bạn thường thấy việc đóng tài nguyên trong khối cuối cùng.


1
Đây là câu trả lời đúng khi tài nguyên trong câu hỏi có một chủ sở hữu duy nhất và không bao giờ có tham chiếu đến nó "bị đánh cắp" bởi mã khác.
Steve Jessop

3

Có một chú thích @Cleanup trong Lombok gần giống với các hàm hủy của C ++:

@Cleanup
ResourceClass resource = new ResourceClass();

Khi xử lý nó (tại thời gian biên dịch), Lombok chèn try-finallykhối thích hợp để resource.close()được gọi, khi thực thi rời khỏi phạm vi của biến. Bạn cũng có thể chỉ định rõ ràng một phương thức khác để giải phóng tài nguyên, ví dụ resource.dispose():

@Cleanup("dispose")
ResourceClass resource = new ResourceClass();

2
Ưu điểm tôi thấy là sẽ có ít lồng nhau hơn (Điều này có thể có ý nghĩa nếu bạn có nhiều đối tượng yêu cầu "phá hủy").
Alexey

Một khối tài nguyên thử có thể có nhiều tài nguyên trong một lần bắn
Alexander - Tái lập Monica

1
Nhưng nó không thể có hướng dẫn giữa chúng.
Alexey

Hội chợ. Tôi cho rằng sau đó khuyến nghị là thích dùng thử với tài nguyên, thậm chí cho nhiều tài nguyên, trừ khi cần có hướng dẫn giữa chúng buộc bạn phải tạo khối thử tài nguyên mới (tăng lồng nhau), sau đó sử dụng@Cleanup
Alexander - Tái tạo Monica

2

Phương thức gần nhất tương đương với hàm hủy trong Java là phương thức Finalize () . Sự khác biệt lớn đối với một công cụ hủy diệt truyền thống là bạn không thể chắc chắn khi nào nó sẽ được gọi, vì đó là trách nhiệm của người thu gom rác. Tôi thực sự khuyên bạn nên đọc kỹ điều này trước khi sử dụng nó, vì các mẫu RAIA điển hình của bạn để xử lý tệp và do đó sẽ không hoạt động đáng tin cậy với việc hoàn tất ().


2

Nhiều câu trả lời tuyệt vời ở đây, nhưng có một số thông tin bổ sung về lý do tại sao bạn nên tránh sử dụng Finalize () .

Nếu JVM thoát do System.exit()hoặc Runtime.getRuntime().exit(), bộ hoàn thiện sẽ không được chạy theo mặc định. Từ Javadoc cho Runtime.exit () :

Trình tự tắt máy của máy ảo bao gồm hai giai đoạn. Trong giai đoạn đầu, tất cả các móc tắt máy đã đăng ký, nếu có, được bắt đầu theo một số thứ tự không xác định và được phép chạy đồng thời cho đến khi chúng kết thúc. Trong giai đoạn thứ hai, tất cả các công cụ hoàn thiện chưa được kích hoạt sẽ được chạy nếu tính năng hoàn thiện khi thoát đã được bật. Một khi điều này được thực hiện thì máy ảo dừng lại.

Bạn có thể gọi System.runFinalization()nhưng nó chỉ làm cho "một nỗ lực tốt nhất để hoàn thành tất cả các quyết toán xuất sắc" - không phải là một sự đảm bảo.

Có một System.runFinalizersOnExit()phương pháp, nhưng đừng sử dụng nó - nó không an toàn, không dùng nữa từ lâu.


1

Nếu bạn đang viết một Applet Java, bạn có thể ghi đè phương thức "hủy ()" của Applet. Nó là...

 * Called by the browser or applet viewer to inform
 * this applet that it is being reclaimed and that it should destroy
 * any resources that it has allocated. The stop() method
 * will always be called before destroy().

Rõ ràng không phải là những gì bạn muốn, nhưng có thể là những gì người khác đang tìm kiếm.


1

Chỉ cần suy nghĩ về câu hỏi ban đầu ... mà tôi nghĩ rằng chúng ta có thể kết luận từ tất cả các câu trả lời đã học khác, và từ Java hiệu quả thiết yếu của Bloch , mục 7, "Tránh các công cụ hoàn thiện", tìm kiếm giải pháp cho một câu hỏi hợp pháp theo cách mà không phù hợp với ngôn ngữ Java ...:

... sẽ không phải là một giải pháp khá rõ ràng để làm những gì OP thực sự muốn là giữ tất cả các đối tượng của bạn cần được đặt lại theo kiểu "playpen", mà tất cả các đối tượng không thể đặt lại khác chỉ tham chiếu qua một số loại của đối tượng accessor ...

Và sau đó khi bạn cần "thiết lập lại", bạn ngắt kết nối playpen hiện có và tạo một cái mới: tất cả các trang web của các đối tượng trong playpen được truyền đi, không bao giờ quay lại và một ngày nào đó sẽ được thu thập bởi GC.

Nếu bất kỳ đối tượng nào trong số này là Closeable(hoặc không, nhưng có một closephương thức), bạn có thể đặt chúng vào Bagtrong playpen khi chúng được tạo (và có thể mở), và hành động cuối cùng của trình truy cập trước khi cắt playpen sẽ xảy ra thông qua tất cả việc Closeablesđóng chúng ...?

Mã có thể sẽ trông giống như thế này:

accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );

closeCloseablescó lẽ sẽ là một phương thức chặn, có thể liên quan đến chốt (ví dụ CountdownLatch), để xử lý (và chờ khi thích hợp) cho bất kỳ Runnables/ Callablestrong bất kỳ luồng nào cụ thể để Playpenkết thúc khi thích hợp, đặc biệt là trong luồng JavaFX.


0

Mặc dù đã có những tiến bộ đáng kể trong công nghệ GC của Java, bạn vẫn cần chú ý đến các tài liệu tham khảo của mình. Vô số trường hợp mô hình tham chiếu có vẻ tầm thường mà thực sự là những con chuột làm tổ dưới mui xe xuất hiện trong tâm trí.

Từ bài đăng của bạn, có vẻ như bạn đang cố gắng thực hiện phương pháp đặt lại cho mục đích tái sử dụng đối tượng (đúng không?). Các đối tượng của bạn đang nắm giữ bất kỳ loại tài nguyên nào khác cần được dọn sạch (tức là các luồng phải được đóng lại, bất kỳ đối tượng được gộp hoặc mượn nào phải được trả lại)? Nếu điều duy nhất bạn lo lắng là bộ nhớ dealloc thì tôi sẽ xem xét lại cấu trúc đối tượng của mình và cố gắng xác minh rằng các đối tượng của tôi là các cấu trúc khép kín sẽ được dọn sạch tại thời điểm GC.



0

Không có Java không có bất kỳ hàm hủy nào. Lý do chính đằng sau nó trong Java là Trình thu gom rác luôn hoạt động thụ động trong nền và tất cả các đối tượng được tạo trong bộ nhớ heap, đó là nơi mà GC hoạt động. phải gọi hàm xóa một cách rõ ràng vì không có trình thu gom rác như thứ.


-3

Tôi đã từng chủ yếu đối phó với C ++ và đó là điều dẫn tôi đến việc tìm kiếm một kẻ hủy diệt. Tôi đang sử dụng JAVA rất nhiều. Những gì tôi đã làm và nó có thể không phải là trường hợp tốt nhất cho mọi người, nhưng tôi đã thực hiện hàm hủy của riêng mình bằng cách đặt lại tất cả các giá trị về 0 hoặc có mặc định thông qua một hàm.

Thí dụ:

public myDestructor() {

variableA = 0; //INT
variableB = 0.0; //DOUBLE & FLOAT
variableC = "NO NAME ENTERED"; //TEXT & STRING
variableD = false; //BOOL

}

Lý tưởng là điều này sẽ không hoạt động cho tất cả các tình huống, nhưng nếu có các biến toàn cục thì nó sẽ hoạt động miễn là bạn không có hàng tấn chúng.

Tôi biết tôi không phải là lập trình viên Java giỏi nhất, nhưng dường như nó đang làm việc cho tôi.


Cố gắng sử dụng các đối tượng bất biến nhiều hơn, sau khi bạn 'hiểu được', tất cả sẽ có ý nghĩa hơn :)
R. van Twisk

5
Điều này không quá sai vì nó là vô nghĩa - tức là không đạt được gì. Nếu chương trình của bạn yêu cầu các kiểu thô được đặt lại để hoạt động chính xác, thì các thể hiện lớp của bạn được đặt trong phạm vi không chính xác, có nghĩa là bạn có thể gán lại các thuộc tính của đối tượng hiện tại cho các thuộc tính của đối tượng mới, mà không tạo Đối tượng mới ().
simo.3792

1
Ngoại trừ việc có rất nhiều nhu cầu để đặt lại các biến. Tôi đã chọn công cụ hủy tên vì nó phù hợp với những gì tôi đang làm. Nó không đạt được điều gì, chỉ là bạn không hiểu gì
ChristianGLong
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.