Điều quan trọng là phải tách việc xử lý ra khỏi việc thu gom rác. Chúng là những thứ hoàn toàn riêng biệt, có một điểm chung mà tôi sẽ đến sau một phút.
Dispose
, thu gom và hoàn thiện rác
Khi bạn viết một using
câu lệnh, nó chỉ đơn giản là đường cú pháp cho một khối try / final để Dispose
được gọi ngay cả khi mã trong phần thân của using
câu lệnh ném ra một ngoại lệ. Nó không có nghĩa là đối tượng là rác được thu thập ở cuối khối.
Xử lý là về tài nguyên không được quản lý ( tài nguyên không phải bộ nhớ). Đây có thể là các điều khiển giao diện người dùng, kết nối mạng, xử lý tệp, v.v. Đây là những tài nguyên hạn chế, vì vậy bạn thường muốn phát hành chúng ngay khi có thể. Bạn nên triển khai IDisposable
bất cứ khi nào kiểu của bạn "sở hữu" một tài nguyên không được quản lý, trực tiếp (thường là thông qua một IntPtr
) hoặc gián tiếp (ví dụ: thông qua a Stream
, a, SqlConnection
v.v.).
Bản thân việc thu gom rác chỉ là về bộ nhớ - với một chút thay đổi nhỏ. Bộ thu gom rác có thể tìm các đối tượng không còn có thể tham chiếu được nữa và giải phóng chúng. Mặc dù vậy, nó không tìm kiếm rác - chỉ khi nó phát hiện ra rằng nó cần (ví dụ: nếu một "thế hệ" của heap hết bộ nhớ).
Vòng xoắn đang hoàn thiện . Trình thu gom rác giữ một danh sách các đối tượng không còn có thể truy cập được nữa, nhưng có một trình hoàn thiện (được viết bằng ~Foo()
C #, hơi khó hiểu - chúng không giống như các trình hủy C ++). Nó chạy các trình hoàn thiện trên các đối tượng này, đề phòng trường hợp chúng cần dọn dẹp thêm trước khi bộ nhớ của chúng được giải phóng.
Finalizers hầu như luôn được sử dụng để dọn dẹp tài nguyên trong trường hợp người dùng loại này đã quên vứt bỏ nó một cách có trật tự. Vì vậy, nếu bạn mở một FileStream
nhưng quên gọi Dispose
hoặc Close
, trình hoàn thiện cuối cùng sẽ phát hành trình xử lý tệp cơ bản cho bạn. Theo ý kiến của tôi, trong một chương trình được viết tốt, những người hoàn thiện hầu như không bao giờ được kích hoạt.
Đặt một biến thành null
Một điểm nhỏ khi đặt biến thành null
- điều này hầu như không bao giờ bắt buộc vì lợi ích của việc thu gom rác. Đôi khi bạn có thể muốn làm điều đó nếu đó là một biến thành viên, mặc dù theo kinh nghiệm của tôi, hiếm khi "một phần" của một đối tượng không còn cần thiết nữa. Khi đó là một biến cục bộ, JIT thường đủ thông minh (ở chế độ phát hành) để biết khi nào bạn sẽ không sử dụng lại tham chiếu. Ví dụ:
StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();
// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);
// These aren't helping at all!
x = null;
sb = null;
// Assume that x and sb aren't used here
Thời điểm mà bạn có thể đáng giá để đặt một biến cục bộ null
là khi bạn đang ở trong một vòng lặp và một số nhánh của vòng lặp cần sử dụng biến nhưng bạn biết rằng bạn đã đạt đến điểm mà bạn không. Ví dụ:
SomeObject foo = new SomeObject();
for (int i=0; i < 100000; i++)
{
if (i == 5)
{
foo.DoSomething();
// We're not going to need it again, but the JIT
// wouldn't spot that
foo = null;
}
else
{
// Some other code
}
}
Triển khai IDisposable / finalizers
Vì vậy, các loại của riêng bạn có nên triển khai trình hoàn thiện không? Gần như chắc chắn là không. Nếu bạn chỉ gián tiếp nắm giữ các tài nguyên không được quản lý (ví dụ: bạn đã có một FileStream
biến là thành viên) thì việc thêm trình hoàn thiện của riêng bạn sẽ không giúp ích gì: luồng gần như chắc chắn sẽ đủ điều kiện để thu gom rác khi đối tượng của bạn có, vì vậy bạn chỉ có thể dựa vào FileStream
có trình hoàn thiện (nếu cần - nó có thể đề cập đến thứ khác, v.v.). Nếu bạn muốn nắm giữ một tài nguyên không được quản lý "gần như" trực tiếp, đó SafeHandle
là bạn của bạn - bạn phải mất một chút thời gian để tiếp tục, nhưng điều đó có nghĩa là bạn sẽ gần như không bao giờ cần viết lại bản hoàn thiện . Bạn thường chỉ cần một bản hoàn thiện nếu bạn thực sự có khả năng xử lý trực tiếp một tài nguyên (an IntPtr
) và bạn nên chuyển sangSafeHandle
Càng sớm càng tốt. (Có hai liên kết ở đó - lý tưởng là hãy đọc cả hai.)
Joe Duffy có một bộ hướng dẫn rất dài về những người hoàn thiện và IDisposable (được viết chung với rất nhiều người thông minh) rất đáng đọc. Cần lưu ý rằng nếu bạn niêm phong các lớp của mình, nó sẽ làm cho cuộc sống dễ dàng hơn rất nhiều: mẫu ghi đè Dispose
để gọi một Dispose(bool)
phương thức ảo mới, v.v. chỉ phù hợp khi lớp của bạn được thiết kế để kế thừa.
Điều này hơi lan man, nhưng vui lòng yêu cầu làm rõ nơi bạn muốn một số :)