Làm thế nào để một trình thu gom rác ngăn không cho toàn bộ bộ nhớ được quét trên mỗi bộ sưu tập?


16

Một số (ít nhất là bộ thu gom rác của Mono và .NET) có vùng nhớ ngắn hạn mà chúng thường quét và vùng nhớ thứ cấp mà chúng quét ít thường xuyên hơn. Mono gọi đây là một vườn ươm.

Để tìm ra những đối tượng nào có thể được xử lý, họ quét tất cả các đối tượng bắt đầu từ gốc, ngăn xếp và các thanh ghi và loại bỏ tất cả các đối tượng không còn được tham chiếu nữa.

Câu hỏi của tôi là làm thế nào họ ngăn chặn tất cả bộ nhớ sử dụng được quét trên mỗi bộ sưu tập? Về nguyên tắc, cách duy nhất để tìm ra những đối tượng không sử dụng nữa là quét tất cả các đối tượng và tất cả các tham chiếu của chúng. Tuy nhiên, điều này sẽ ngăn hệ điều hành hoán đổi bộ nhớ mặc dù ứng dụng không sử dụng và cảm thấy như một khối lượng công việc khổng lồ cần phải thực hiện, cũng cho "Bộ sưu tập vườn ươm". Nó không cảm thấy như họ chiến thắng nhiều bằng cách sử dụng một vườn ươm.

Tôi có thiếu một cái gì đó hay là trình thu gom rác thực sự quét mọi đối tượng và mọi tham chiếu mỗi khi nó thực hiện một bộ sưu tập?


1
một tổng quan đẹp là trong một bài viết Điều chỉnh bộ sưu tập nghệ thuật rác được viết bởi Angelika Langer. Chính thức, đó là về cách nó được thực hiện trong Java, nhưng các khái niệm được trình bày có khá nhiều thuyết bất khả tri về ngôn ngữ
gnat

Câu trả lời:


14

Các quan sát cơ bản cho phép thu gom rác thế hệ để tránh phải quét tất cả các đối tượng thế hệ cũ là:

  1. Sau một bộ sưu tập, tất cả các đối tượng vẫn còn tồn tại sẽ thuộc một thế hệ tối thiểu (ví dụ: trong .net, sau bộ sưu tập Gen0, tất cả các đối tượng là Gen1 hoặc Gen2; sau bộ sưu tập Gen1 hoặc Gen2, tất cả các đối tượng là Gen2).
  2. Một đối tượng, hoặc một phần của nó, chưa được viết vì một bộ sưu tập đã quảng bá mọi thứ lên thế hệ N hoặc cao hơn không thể chứa bất kỳ tham chiếu nào đến các đối tượng thuộc thế hệ thấp hơn.
  3. Nếu một đối tượng đã đạt đến một thế hệ nhất định, nó không cần phải được xác định là có thể tiếp cận để đảm bảo duy trì nó khi thu thập các thế hệ thấp hơn.

Trong nhiều khung công tác GC, trình thu gom rác có thể gắn cờ các đối tượng hoặc các phần của chúng theo cách mà lần đầu tiên ghi vào chúng sẽ kích hoạt mã đặc biệt để ghi lại thực tế rằng chúng đã được sửa đổi. Một đối tượng hoặc một phần của nó đã được sửa đổi, bất kể thế hệ của nó, phải được quét trong bộ sưu tập tiếp theo, vì nó có thể chứa các tham chiếu đến các đối tượng mới hơn. Mặt khác, có rất nhiều đối tượng cũ không được sửa đổi giữa các bộ sưu tập. Thực tế là các bản quét thế hệ thấp hơn có thể bỏ qua các đối tượng như vậy có thể cho phép các bản quét như vậy hoàn thành nhanh hơn nhiều so với các cách khác.

Lưu ý, btw, ngay cả khi người ta không thể phát hiện khi các đối tượng được sửa đổi và sẽ phải quét mọi thứ trên mỗi lần vượt qua GC, bộ sưu tập rác thế hệ vẫn có thể cải thiện hiệu suất giai đoạn "quét" của trình thu gom nén. Trong một số môi trường nhúng (đặc biệt là những môi trường có ít hoặc không có sự khác biệt về tốc độ giữa truy cập bộ nhớ ngẫu nhiên và tuần tự), việc di chuyển các khối bộ nhớ xung quanh là tương đối đắt so với tham chiếu gắn thẻ. Do đó, ngay cả khi giai đoạn "đánh dấu" không thể được tăng tốc bằng cách sử dụng bộ sưu tập thế hệ, việc tăng tốc độ "quét" có thể đáng giá.


di chuyển các khối bộ nhớ xung quanh là tốn kém trong bất kỳ hệ thống nào, vì vậy việc cải thiện khả năng quét là một lợi ích ngay cả trên hệ thống CPU Quad Ghz của bạn.
gbjbaanb

@gbjbaanb: Trong nhiều trường hợp, chi phí quét mọi thứ để tìm các vật thể sống sẽ rất đáng kể và gây khó chịu ngay cả khi việc di chuyển các vật thể hoàn toàn miễn phí. Do đó, một trong những thực tế nên tránh quét các đối tượng cũ. Mặt khác, kiềm chế việc nén các đối tượng cũ là một tối ưu hóa đơn giản có thể được thực hiện ngay cả trên các khung đơn giản. BTW, nếu một người đang thiết kế khung GC cho một hệ thống nhúng nhỏ, hỗ trợ khai báo cho các đối tượng bất biến có thể hữu ích. Theo dõi xem một đối tượng có thể thay đổi có thay đổi hay không, nhưng người ta có thể làm tốt để ...
supercat

... Đơn giản chỉ cần giả định rằng các đối tượng có thể thay đổi cần được quét mỗi lần vượt qua GC nhưng các đối tượng bất biến thì không. Ngay cả khi cách duy nhất để xây dựng một đối tượng bất biến là xây dựng một "nguyên mẫu" trong không gian có thể thay đổi và sau đó sao chép nó, thì thao tác sao chép thêm duy nhất có thể tránh được việc phải quét đối tượng trong các hoạt động GC trong tương lai.
supercat

Ngẫu nhiên, hiệu suất thu gom rác trên các triển khai BASIC có nguồn gốc từ năm 1980 của Microsoft cho 6502 bộ vi xử lý (và có lẽ cả những bộ khác) có thể được tăng cường đáng kể trong một số trường hợp, nếu một chương trình tạo ra nhiều chuỗi không bao giờ thay đổi, hãy sao chép "tiếp theo phân bổ chuỗi "con trỏ tới con trỏ" đỉnh của không gian chuỗi ". Thay đổi như vậy sẽ ngăn người thu gom rác kiểm tra bất kỳ chuỗi cũ nào để xem liệu chúng có còn cần thiết không. Commodore 64 hầu như không phải là công nghệ cao, nhưng GC "thế hệ" như vậy sẽ giúp ích ngay cả ở đó.
supercat

7

Các GC mà bạn đang đề cập đến là những người thu gom rác thế hệ . Họ được thiết kế để tận dụng tối đa một quan sát được gọi là "tỷ lệ tử vong ở trẻ sơ sinh" hoặc "giả thuyết thế hệ", điều đó có nghĩa là hầu hết các đối tượng trở nên không thể tiếp cận rất nhanh. Họ thực sự quét bắt đầu từ gốc, nhưng bỏ qua tất cả các đối tượng cũ . Do đó, họ không cần quét hầu hết các đối tượng trong bộ nhớ, họ chỉ quét các đối tượng trẻ (với chi phí không phát hiện các đối tượng cũ không thể truy cập, ít nhất là không tại thời điểm đó).

"Nhưng đó là sai", tôi nghe bạn hét lên, "những đồ vật cũ có thể và có liên quan đến những đồ vật trẻ". Bạn đã đúng, và có một số giải pháp cho vấn đề đó, tất cả đều xoay quanh việc thu thập kiến ​​thức, nhanh chóng và hiệu quả, những đối tượng cũ phải được kiểm tra và an toàn để bỏ qua. Chúng sôi sục để ghi lại các đối tượng, hoặc các phạm vi nhỏ (lớn hơn các đối tượng, nhưng nhỏ hơn nhiều so với toàn bộ) bộ nhớ chứa con trỏ tới các thế hệ trẻ. Những người khác đã mô tả những thứ đó tốt hơn tôi nhiều, vì vậy tôi sẽ chỉ cung cấp cho bạn một vài từ khóa: Đánh dấu thẻ, bộ nhớ, viết rào cản. Cũng có những kỹ thuật khác (bao gồm cả giống lai), nhưng những kỹ thuật này bao gồm các phương pháp phổ biến mà tôi biết.


3

Để tìm ra những đối tượng vườn ươm vẫn còn sống, người sưu tầm chỉ cần quét bộ gốc và bất kỳ đối tượng cũ nào đã bị đột biến kể từ bộ sưu tập cuối cùng , vì một đối tượng cũ không bị đột biến gần đây có thể chỉ ra một đối tượng trẻ . Có các thuật toán khác nhau để duy trì thông tin này ở các mức độ chính xác khác nhau (từ một tập hợp chính xác các trường bị đột biến đến một tập hợp các trang có thể xảy ra đột biến), nhưng tất cả chúng đều liên quan đến một loại rào cản ghi : mã chạy trên mọi tham chiếu đột biến trường được cập nhật để cập nhật sổ sách kế toán của GC.


1

Thế hệ thu gom rác lâu đời nhất và đơn giản nhất thực sự đã quét tất cả bộ nhớ và phải dừng tất cả các xử lý khác trong khi họ thực hiện. Các thuật toán sau này đã cải thiện điều này theo nhiều cách khác nhau - làm cho việc sao chép / quét tăng dần hoặc chạy song song. Hầu hết các công cụ thu gom rác hiện đại phân tách các đối tượng thành các thế hệ và quản lý cẩn thận các con trỏ thế hệ chéo để các thế hệ mới hơn có thể được thu thập mà không làm phiền các đối tượng cũ.

Điểm mấu chốt là các trình thu gom rác phối hợp chặt chẽ với trình biên dịch và với phần còn lại của thời gian chạy để duy trì ảo tưởng rằng nó đang xem tất cả bộ nhớ.


Tôi không chắc phương pháp thu gom rác nào đã được sử dụng trong máy tính mini và máy tính lớn trước cuối những năm 1970, nhưng trình thu gom rác Microsoft BASIC, ít nhất là trên các máy 6502, sẽ đặt con trỏ "chuỗi tiếp theo" của nó lên trên bộ nhớ, sau đó tìm kiếm tất cả các tham chiếu chuỗi để tìm địa chỉ cao nhất nằm dưới "con trỏ chuỗi tiếp theo". Chuỗi đó sẽ được sao chép ngay bên dưới "con trỏ chuỗi tiếp theo" và con trỏ đó sẽ được đặt ngay bên dưới nó. Thuật toán sau đó sẽ lặp lại. Có thể mã để jinx các con trỏ cung cấp ...
supercat

... Một cái gì đó giống như bộ sưu tập thế hệ. Đôi khi tôi đã tự hỏi sẽ khó khăn đến mức nào để vá BASIC để thực hiện bộ sưu tập "thế hệ" bằng cách giữ địa chỉ của đầu mỗi thế hệ và thêm một vài thao tác hoán đổi con trỏ trước và sau mỗi chu kỳ GC. Hiệu suất của GC vẫn còn khá tệ, nhưng trong nhiều trường hợp có thể bị cạo từ hàng chục giây xuống còn mười giây.
supercat

-2

Về cơ bản ... GC sử dụng "xô" để phân tách những gì đang sử dụng và những gì không. Khi nó kiểm tra, nó xóa sạch những thứ không được sử dụng và chuyển mọi thứ khác sang thế hệ thứ 2 (được kiểm tra ít thường xuyên hơn thế hệ 1) và sau đó chuyển những thứ vẫn còn được sử dụng trong thế hệ thứ 2 sang thế hệ thứ ba.

Vì vậy, mọi thứ ở thế hệ thứ 3 thường là các đối tượng bị kẹt mở vì một số lý do và GC không kiểm tra ở đó rất thường xuyên.


1
Nhưng làm thế nào để nó biết những đối tượng đang sử dụng?
Pieter van Ginkel

Nó theo dõi những đối tượng nào có thể truy cập được từ mã có thể truy cập. Khi một đối tượng không còn có thể truy cập được từ bất kỳ mã nào có thể thực thi (giả sử mã cho một phương thức đã trả về) thì GC biết rằng nó an toàn để thu thập
JohnL

Cả hai bạn đang mô tả làm thế nào các GC là chính xác, không phải là cách họ hiệu quả. Đánh giá từ câu hỏi, OP biết điều đó hoàn toàn rõ ràng.

@delnan vâng Tôi đã trả lời câu hỏi làm thế nào nó biết những vật thể nào đang được sử dụng, đó là những gì trong bình luận của Peter.
JohnL

-5

Thuật toán thường được sử dụng bởi GC này là đánh dấu và quét Naïve

bạn cũng nên lưu ý rằng thực tế đây không phải do chính C # quản lý, mà bởi cái gọi là CLR .


Đó là cảm giác mà tôi có được khi đọc về người thu gom rác của Mono. Tuy nhiên, điều tôi không hiểu là tại sao nếu họ đang quét bộ công việc hoàn chỉnh trên từng bộ sưu tập, họ có một bộ sưu tập thế hệ mà bộ sưu tập GEN-0 rất nhanh. Làm thế nào điều này có thể nhanh chóng với một bộ 2GB hoạt động?
Pieter van Ginkel

tốt, GC thực sự cho mono là Sgen, bạn nên đọc mono-project.com/Generational_GC hoặc một số bài viết trực tuyến schani.wordpress.com/tag/mono infoq.com/news/2011/01/SGen , vấn đề là công nghệ mới này như CLR và CLI có thiết kế mô-đun thực sự, ngôn ngữ trở thành một cách để thể hiện một cái gì đó cho CLR và không phải là một cách để tạo mã nhị phân. Câu hỏi của bạn là về chi tiết triển khai chứ không phải về thuật toán, bởi vì thuật toán vẫn không có triển khai, bạn chỉ nên đọc các bài viết và bài viết kỹ thuật từ Mono, không ai khác.
dùng827992

Tôi bối rối. Chiến lược mà người thu gom rác sử dụng không phải là một thuật toán?
Pieter van Ginkel

2
-1 Ngừng gây nhầm lẫn cho OP. Rằng GC là một phần của CLR và không cụ thể về ngôn ngữ là không liên quan. Một GC chủ yếu được đặc trưng bởi cách nó đưa ra đống và xác định khả năng tiếp cận, và sau này là tất cả về (các) thuật toán được sử dụng cho điều đó. Mặc dù có thể có nhiều triển khai của một thuật toán và bạn không nên bị cuốn vào các chi tiết triển khai, chỉ riêng thuật toán sẽ xác định có bao nhiêu đối tượng được quét. Một thế hệ GC chỉ đơn giản là một thuật toán + bố trí heap cố gắng sử dụng "giả thuyết thế hệ" (hầu hết các đối tượng đều chết trẻ). Đây không phải là ngây thơ.

4
Thuật toán! = Thực hiện thực sự, nhưng một triển khai chỉ có thể đi chệch hướng này trước khi nó trở thành một triển khai của một thuật toán khác. Một mô tả thuật toán, trong thế giới GC, rất cụ thể và bao gồm những thứ như không quét toàn bộ đống trên bộ sưu tập vườn ươm và cách tìm và lưu trữ các con trỏ thế hệ. Đúng là một thuật toán không cho bạn biết một bước cụ thể của thuật toán sẽ mất bao lâu, nhưng điều đó hoàn toàn không liên quan đến câu hỏi nà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.