Bạn thực sự không thể đưa ra tuyên bố về cách thích hợp để sử dụng tất cả các triển khai GC. Họ khác nhau rất nhiều. Vì vậy, tôi sẽ nói chuyện với .NET mà bạn đã đề cập ban đầu.
Bạn phải biết hành vi của GC khá thân mật để làm điều này với bất kỳ logic hoặc lý do.
Lời khuyên duy nhất về bộ sưu tập tôi có thể đưa ra là: Đừng bao giờ làm điều đó.
Nếu bạn thực sự biết các chi tiết phức tạp của GC, bạn sẽ không cần lời khuyên của tôi nên điều đó không thành vấn đề. Nếu bạn chưa biết với độ tin cậy 100% thì nó sẽ có ích, và phải tìm kiếm trực tuyến và tìm câu trả lời như thế này: Bạn không nên gọi cho GC.Collect , hoặc cách khác: Bạn nên tìm hiểu chi tiết về cách thức hoạt động của GC từ trong ra ngoài, và chỉ sau đó bạn mới biết câu trả lời .
Có một nơi an toàn, có ý nghĩa khi sử dụng GC.Collect :
GC.Collect là một API có sẵn mà bạn có thể sử dụng để định hình thời gian của mọi thứ. Bạn có thể lập hồ sơ một thuật toán, thu thập và lập hồ sơ một thuật toán khác ngay sau đó khi biết GC của thuật toán đầu tiên không xảy ra trong lần thứ hai của bạn làm sai lệch kết quả.
Kiểu hồ sơ này là lần duy nhất tôi từng đề nghị thu thập thủ công cho bất kỳ ai.
Dù sao đi nữa
Một trường hợp sử dụng có thể là nếu bạn tải những thứ thực sự lớn, chúng sẽ kết thúc trong Heap đối tượng lớn sẽ đi thẳng đến Gen 2, mặc dù vậy, Gen 2 lại dành cho các đối tượng tồn tại lâu vì nó thu thập ít thường xuyên hơn. Nếu bạn biết rằng bạn đang tải các đối tượng tồn tại ngắn vào Gen 2 vì bất kỳ lý do gì, bạn có thể xóa chúng nhanh hơn để giữ cho Gen 2 của bạn nhỏ hơn và bộ sưu tập của nó nhanh hơn.
Đây là ví dụ tốt nhất tôi có thể đưa ra, và nó không tốt - áp lực LOH mà bạn xây dựng ở đây sẽ gây ra các bộ sưu tập thường xuyên hơn và các bộ sưu tập thường xuyên như vậy - rất có thể nó sẽ xóa LOH giống như nhanh như bạn đã thổi nó ra với các đối tượng tạm thời. Tôi chỉ đơn giản là không tin tưởng bản thân mình để đoán một tần số bộ sưu tập tốt hơn so với GC bản thân - điều chỉnh bởi những người xa xa thông minh hơn I.
Vì vậy, hãy nói về một số ngữ nghĩa và cơ chế trong .NET GC ... hoặc ..
Tất cả mọi thứ tôi nghĩ rằng tôi biết về .NET GC
Xin vui lòng, bất cứ ai tìm thấy lỗi ở đây - hãy sửa cho tôi. Phần lớn các GC được biết đến là ma thuật đen và trong khi tôi cố gắng bỏ qua các chi tiết mà tôi không chắc chắn, tôi có lẽ vẫn còn một số điều sai.
Dưới đây là cố tình thiếu rất nhiều chi tiết tôi không chắc chắn, cũng như một lượng thông tin lớn hơn nhiều mà tôi chỉ đơn giản là không biết. Sử dụng thông tin này có nguy cơ của riêng bạn.
Khái niệm GC
.NET GC xảy ra vào những thời điểm không nhất quán, đó là lý do tại sao nó được gọi là "không xác định", điều này có nghĩa là bạn không thể dựa vào nó để xảy ra vào những thời điểm cụ thể. Nó cũng là một trình thu gom rác thế hệ, có nghĩa là nó phân vùng các đối tượng của bạn thành số lượng GC mà chúng đã trải qua.
Các đối tượng trong heap Gen 0 đã sống qua 0 bộ sưu tập, chúng đã được tạo mới nên gần đây không có bộ sưu tập nào xảy ra kể từ khi khởi tạo. Các đối tượng trong heap Gen 1 của bạn đã sống qua một lần thu thập và các đối tượng tương tự trong heap Gen 2 của bạn đã sống qua 2 lần thu thập.
Bây giờ đáng chú ý là lý do nó đủ điều kiện cho các thế hệ và phân vùng cụ thể phù hợp. .NET GC chỉ nhận ra ba thế hệ này, bởi vì bộ sưu tập vượt qua ba đống này hoàn toàn khác nhau. Một số đối tượng có thể sống sót qua bộ sưu tập hàng ngàn lần. GC chỉ để những thứ này ở phía bên kia của phân vùng heap Gen 2, không có điểm nào trong việc phân vùng chúng ở bất cứ đâu xa hơn vì chúng thực sự là Gen 44; bộ sưu tập vượt qua chúng giống như mọi thứ trong đống gen 2.
Có những mục đích ngữ nghĩa cho các thế hệ cụ thể này, cũng như các cơ chế được thực hiện nhằm tôn vinh những thế hệ này và tôi sẽ đến với những người đó ngay lập tức.
Những gì trong một bộ sưu tập
Khái niệm cơ bản của một bộ sưu tập GC là nó kiểm tra từng đối tượng trong một không gian heap để xem liệu vẫn còn các tham chiếu trực tiếp (gốc GC) cho các đối tượng này hay không. Nếu một gốc GC được tìm thấy cho một đối tượng, điều đó có nghĩa là mã hiện đang thực thi vẫn có thể tiếp cận và sử dụng đối tượng đó, vì vậy nó không thể bị xóa. Tuy nhiên, nếu không tìm thấy gốc GC cho một đối tượng, điều đó có nghĩa là quá trình đang chạy không còn cần đối tượng nữa, vì vậy nó có thể loại bỏ nó để giải phóng bộ nhớ cho các đối tượng mới.
Bây giờ sau khi hoàn thành việc dọn dẹp một loạt các vật thể và để lại một mình, sẽ có một tác dụng phụ đáng tiếc: Khoảng cách không gian trống giữa các vật thể sống nơi những người chết đã được gỡ bỏ. Sự phân mảnh bộ nhớ này nếu chỉ để lại một mình sẽ làm lãng phí bộ nhớ, do đó, các bộ sưu tập thường sẽ thực hiện cái gọi là "nén" trong đó chúng lấy tất cả các vật thể còn lại và ép chúng lại với nhau trong một đống để bộ nhớ trống nằm liền kề ở một bên của đống cho Gen 0.
Bây giờ được đưa ra ý tưởng về 3 đống bộ nhớ, tất cả được phân chia theo số lượng bộ sưu tập họ đã trải qua, hãy nói về lý do tại sao các phân vùng này tồn tại.
Bộ sưu tập Gen 0
Gen 0 là đối tượng mới nhất tuyệt đối, có xu hướng rất nhỏ - vì vậy bạn có thể thu thập nó rất thường xuyên . Tần suất đảm bảo heap vẫn nhỏ và các bộ sưu tập rất nhanh vì chúng thu thập trên một đống nhỏ như vậy. Điều này dựa trên ít nhiều dựa trên một heuristic tuyên bố: Phần lớn các đối tượng tạm thời mà bạn tạo ra là rất tạm thời, do đó tạm thời chúng sẽ không còn được sử dụng hoặc tham chiếu gần như ngay lập tức sau khi sử dụng và do đó có thể được thu thập.
Bộ sưu tập Gen 1
Gen 1 là các đối tượng không thuộc loại đối tượng rất tạm thời này, có thể vẫn còn tồn tại khá ngắn, bởi vì - một phần lớn các đối tượng được tạo ra không được sử dụng lâu dài. Do đó, Gen 1 cũng thu thập khá thường xuyên, một lần nữa giữ cho nó rất nhỏ để bộ sưu tập của nó nhanh. Tuy nhiên, giả định ít đối tượng của nó là tạm thời hơn Gen 0, do đó, nó thu thập ít thường xuyên hơn Gen 0
Tôi sẽ nói rằng tôi thực sự không biết các cơ chế kỹ thuật khác nhau giữa bộ sưu tập của Gen 0 và của Gen 1, nếu có bất kỳ thứ gì khác ngoài tần số chúng thu thập.
Bộ sưu tập Gen 2
Gen 2 bây giờ phải là mẹ của tất cả các đống phải không? Vâng, vâng, điều đó ít nhiều đúng. Đó là nơi tất cả các đối tượng vĩnh viễn của bạn sống - Main()
ví dụ như đối tượng mà bạn sống và mọi thứ Main()
tham chiếu bởi vì những đối tượng đó sẽ được bắt nguồn cho đến khi bạn Main()
quay trở lại vào cuối quá trình.
Cho rằng Gen 2 là một cái xô cho tất cả mọi thứ mà các thế hệ khác không thể thu thập được, các đối tượng của nó phần lớn là vĩnh viễn hoặc ít nhất là tồn tại lâu dài. Vì vậy, nhận ra rất ít những gì trong Gen 2 thực sự sẽ là thứ có thể được thu thập, nó không cần phải thu thập thường xuyên. Điều này cho phép bộ sưu tập của nó cũng chậm hơn, vì nó thực hiện rất ít thường xuyên hơn. Vì vậy, về cơ bản, đây là nơi họ đã giải quyết tất cả các hành vi bổ sung cho các tình huống kỳ lạ, bởi vì họ có thời gian để thực hiện chúng.
Đống vật thể lớn
Một ví dụ về các hành vi bổ sung của Gen 2 là nó cũng thực hiện bộ sưu tập trên Heap đối tượng lớn. Cho đến bây giờ tôi đã nói hoàn toàn về Heap đối tượng nhỏ, nhưng thời gian chạy .NET phân bổ những thứ có kích thước nhất định cho một heap riêng vì những gì tôi gọi là nén ở trên. Nén yêu cầu di chuyển các đối tượng xung quanh khi các bộ sưu tập kết thúc trên Heap đối tượng nhỏ. Nếu có một đối tượng 10mb còn sống trong Gen 1, thì sẽ mất nhiều thời gian hơn để hoàn thành việc nén sau khi thu thập, do đó làm chậm bộ sưu tập của Gen 1. Vì vậy, đối tượng 10mb được phân bổ cho Heap đối tượng lớn và được thu thập trong Gen 2 chạy không thường xuyên.
Hoàn thiện
Một ví dụ khác là các đối tượng với quyết toán. Bạn đặt một bộ hoàn thiện vào một đối tượng tham chiếu các tài nguyên nằm ngoài phạm vi của .NETs (tài nguyên không được quản lý). Trình hoàn thiện là cách duy nhất mà GC yêu cầu thu thập tài nguyên không được quản lý - bạn triển khai trình hoàn thiện của mình để thực hiện thu thập / xóa / giải phóng tài nguyên không được quản lý để đảm bảo nó không bị rò rỉ từ quy trình của bạn. Khi GC thực hiện bộ hoàn thiện đối tượng của bạn, thì việc triển khai của bạn sẽ xóa tài nguyên không được quản lý, làm cho GC có khả năng loại bỏ đối tượng của bạn mà không gặp rủi ro rò rỉ tài nguyên.
Cơ chế mà người hoàn thiện thực hiện điều này là bằng cách được tham chiếu trực tiếp trong hàng đợi quyết toán. Khi bộ thực thi phân bổ một đối tượng với bộ hoàn thiện, nó sẽ thêm một con trỏ tới đối tượng đó vào hàng đợi hoàn thiện và khóa đối tượng của bạn tại chỗ (được gọi là ghim) để nén không di chuyển nó sẽ phá vỡ tham chiếu hàng đợi quyết toán. Khi bộ sưu tập xảy ra, cuối cùng đối tượng của bạn sẽ không còn có gốc GC nữa, nhưng việc hoàn thiện phải được thực hiện trước khi có thể được thu thập. Vì vậy, khi đối tượng đã chết, bộ sưu tập sẽ di chuyển tham chiếu của nó từ hàng đợi hoàn thiện và đặt một tham chiếu đến nó trên hàng đợi được gọi là "FReachable". Sau đó bộ sưu tập tiếp tục. Tại một thời điểm "không xác định" khác trong tương lai, một luồng riêng biệt được gọi là luồng Finalizer sẽ đi qua hàng đợi FReachable, thực thi các trình hoàn thiện cho từng đối tượng được tham chiếu. Sau khi kết thúc, hàng đợi FReachable trống và nó đã lật một chút trên tiêu đề của từng đối tượng nói rằng họ không cần hoàn thiện (Bit này cũng có thể được lật bằng tay vớiGC.SuppressFinalize
vốn phổ biến trong Dispose()
các phương thức), tôi cũng nghi ngờ nó đã bỏ ghim các đối tượng, nhưng đừng trích dẫn tôi về điều đó. Bộ sưu tập tiếp theo xuất hiện trên bất kỳ đống vật thể nào trong đó, cuối cùng sẽ thu thập nó. Các bộ sưu tập Gen 0 thậm chí không chú ý đến các đối tượng có bit cần thiết quyết toán đó, nó tự động quảng bá chúng mà không cần kiểm tra gốc của chúng. Một đối tượng chưa được phân phối cần hoàn thiện trong Gen 1, sẽ bị ném vào FReachable
hàng đợi, nhưng bộ sưu tập không làm gì khác với nó, vì vậy, nó sống trong Gen 2. Theo cách này, tất cả các đối tượng có bộ hoàn thiện và không GC.SuppressFinalize
sẽ được thu thập trong Gen 2.