Khi nào tôi nên sử dụng GC.SuppressFinalize ()?


287

Trong .NET, tôi nên sử dụng trong trường hợp GC.SuppressFinalize()nào?

Những lợi thế nào khi sử dụng phương pháp này mang lại cho tôi?


Tôi đã thấy một vài câu hỏi về trình hoàn thiện và IDis Dùng, stackoverflow cũng nên có một cái gì đó về GC.SupressFinalize và các tài liệu tham khảo yếu
Sam Saffron

Tôi không nghĩ các tài liệu tham khảo yếu làm bất cứ điều gì liên quan đến người hoàn thiện - có lẽ bạn nên đăng câu hỏi trực tiếp hơn về họ.
Michael Burr

Tôi có ý định gửi một câu hỏi riêng về các ref yếu, tất cả những điều này có thể liên kết với nhau khi bạn xây dựng các nhóm đối tượng. Ngoài ra tôi nên hỏi một câu hỏi về sự hồi sinh đối tượng ala ReRegisterForFinalize
Sam Saffron

Câu trả lời:


296

SuppressFinalizechỉ nên được gọi bởi một lớp có bộ hoàn thiện. Nó thông báo cho Bộ sưu tập rác (GC) rằng thisđối tượng đã được dọn sạch hoàn toàn.

Mẫu được đề xuất IDisposablekhi bạn có bộ hoàn thiện là:

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose(). 
                // OK to use any private object references
            }
            // Release unmanaged resources.
            // Set large fields to null.                
            disposed = true;
        }
    }

    public void Dispose() // Implement IDisposable
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}

Thông thường, CLR giữ các tab trên các đối tượng bằng bộ hoàn thiện khi chúng được tạo (khiến chúng đắt hơn khi tạo). SuppressFinalizenói với GC rằng đối tượng đã được dọn sạch đúng cách và không cần phải đi vào hàng đợi hoàn thiện. Nó trông giống như một hàm hủy của C ++, nhưng không hoạt động giống như một.

Việc SuppressFinalizetối ưu hóa không phải là nhỏ, vì các đối tượng của bạn có thể sống rất lâu chờ đợi trên hàng đợi hoàn thiện. Đừng cố gắng kêu gọi SuppressFinalizecác đối tượng khác nhớ bạn. Đó là một khiếm khuyết nghiêm trọng đang chờ xảy ra.

Hướng dẫn thiết kế thông báo cho chúng tôi rằng bộ hoàn thiện là không cần thiết nếu đối tượng của bạn thực hiện IDisposable, nhưng nếu bạn có bộ hoàn thiện, bạn nên thực hiện IDisposableđể cho phép dọn dẹp xác định lớp của bạn.

Hầu hết thời gian bạn sẽ có thể thoát khỏi IDisposableđể làm sạch tài nguyên. Bạn chỉ cần một bộ hoàn thiện khi đối tượng của bạn giữ các tài nguyên không được quản lý và bạn cần đảm bảo các tài nguyên đó được dọn sạch.

Lưu ý: Đôi khi các lập trình viên sẽ thêm một bộ hoàn thiện để gỡ lỗi các bản dựng của các IDisposablelớp của riêng họ để kiểm tra mã đó đã xử lý IDisposableđúng đối tượng của họ .

public void Dispose() // Implement IDisposable
{
    Dispose(true);
#if DEBUG
    GC.SuppressFinalize(this);
#endif
}

#if DEBUG
~MyClass() // the finalizer
{
    Dispose(false);
}
#endif

1
Trong đoạn mã đầu tiên, tôi chỉ đăng những mẫu IDis Dùng + bộ hoàn thiện được đề xuất trông như thế nào. Mã gỡ lỗi là tốt, nhưng nó có thể gây mất tập trung. .. Tôi chỉ có thể khuyên bạn nên tránh các công cụ hoàn thiện ngoại trừ các lớp có tài nguyên không được quản lý. Viết mã hoàn thiện an toàn là không tầm thường.
Robert Paulson

1
Xin chào, Tại sao chúng ta cần gọi dispose với false là tham số từ bộ hoàn thiện? Điều gì xảy ra nếu vứt bỏ không bao giờ được gọi và sau đó nó sẽ không xử lý? Điều gì sẽ xảy ra nếu chúng ta chỉ kiểm tra xem đối tượng đã được xử lý hay chưa và thực hiện việc dọn dẹp thực tế.
mơ mộng

3
@Dreamer - nó phụ thuộc vào việc thực hiện của bạn. Nói chung, bạn muốn biết liệu Dispose đang được gọi bởi trình hoàn thiện so với triển khai IDis Dùng.Dispose (). Nếu được gọi từ trình hoàn thiện, bạn phải cho rằng các tham chiếu riêng tư không còn hợp lệ và bạn thực sự không thể làm được gì nhiều. Tuy nhiên, nếu được gọi từ IDis Dùng.Dispose (), bạn biết rằng các tham chiếu vẫn còn hiệu lực.
Robert Paulson

32
Nếu lớp triển khai IDisposablelà không sealed, thì nó sẽ bao gồm lệnh gọi GC.SuppressFinalize(this) ngay cả khi nó không bao gồm một trình hoàn thiện do người dùng định nghĩa . Điều này là cần thiết để đảm bảo ngữ nghĩa phù hợp cho các loại dẫn xuất thêm trình hoàn thiện do người dùng xác định nhưng chỉ ghi đè Dispose(bool)phương thức được bảo vệ .
Sam Harwell

1
Không được sealedđề cập bởi @SamHarwell là quan trọng, đối với các lớp dẫn xuất. Kết quả CodeAnalysis trong ca1816 + ca1063 khi lớp không được niêm phong, nhưng các lớp niêm phong vẫn ổn mà không có SuppressFinalize.
bảnh bao

38

SupressFinalizenói với hệ thống rằng bất kỳ công việc nào đã được thực hiện trong bộ hoàn thiện đã được thực hiện, vì vậy bộ hoàn thiện không cần phải được gọi. Từ các tài liệu .NET:

Các đối tượng triển khai giao diện IDis Dùng một lần có thể gọi phương thức này từ phương thức IDisydia.Dispose để ngăn trình thu gom rác gọi Object.Finalize trên một đối tượng không yêu cầu.

Nói chung, hầu hết mọi Dispose()phương thức đều có thể gọi GC.SupressFinalize(), bởi vì nó sẽ dọn sạch mọi thứ sẽ được dọn sạch trong bộ hoàn thiện.

SupressFinalizechỉ là một cái gì đó cung cấp tối ưu hóa cho phép hệ thống không bận tâm xếp hàng đối tượng vào luồng hoàn thiện. Một văn bản Dispose()/ quyết toán đúng sẽ hoạt động đúng có hoặc không có cuộc gọi đến GC.SupressFinalize().


2

Phương thức đó phải được gọi trên Disposephương thức của các đối tượng thực hiện IDisposable, theo cách này, GC sẽ không gọi bộ hoàn thiện vào lần khác nếu có người gọi Disposephương thức đó.

Xem: Phương pháp GC.SuppressFinalize (Object) - Microsoft Docs


9
Tôi nghĩ rằng "Phải" là sai - thậm chí là "không nên" - Chỉ là trong một số trường hợp, bạn có thể loại bỏ chi phí xếp hàng / hoàn thiện đối tượng.
Cơ bản

1
Dispose(true);
GC.SuppressFinalize(this);

Nếu đối tượng có bộ hoàn thiện, .net sẽ đặt tham chiếu trong hàng đợi hoàn thiện.

Vì chúng tôi có cuộc gọi Dispose(ture), đối tượng rõ ràng, vì vậy chúng tôi không cần hàng đợi quyết toán để thực hiện công việc này.

Vì vậy, gọi GC.SuppressFinalize(this)loại bỏ tham chiếu trong hàng đợi quyết toán.


0

Nếu một lớp, hoặc bất cứ thứ gì có nguồn gốc từ nó, có thể giữ tham chiếu trực tiếp cuối cùng đến một đối tượng bằng bộ hoàn thiện, thì GC.SuppressFinalize(this)hoặc GC.KeepAlive(this)sẽ được gọi trên đối tượng sau bất kỳ thao tác nào có thể bị ảnh hưởng bất lợi bởi bộ hoàn thiện đó, do đó đảm bảo rằng bộ hoàn thiện đã thắng Sẽ chạy cho đến khi hoạt động đó hoàn tất.

Chi phí GC.KeepAlive()GC.SuppressFinalize(this)về cơ bản là giống nhau trong bất kỳ lớp nào không có bộ hoàn thiện và các lớp thường có bộ hoàn thiện nên thường gọi GC.SuppressFinalize(this), vì vậy sử dụng hàm sau làm bước cuối cùng Dispose()có thể không phải lúc nào cũng cần thiết, nhưng nó sẽ không sai.

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.