Làm thế nào để một trình thu gom rác đồng thời xử lý các biến?


8

Hãy nói rằng nó là một trình thu gom rác đánh dấu đồng thời.

Khi GC xử lý các con trỏ liên tục, nó chỉ đi qua chúng (bắt đầu từ gốc) và đánh dấu mọi khối dữ liệu gặp phải. Sau đó quét tất cả mọi thứ không đánh dấu. Mã khách hàng nên đánh dấu các khối dữ liệu mà nó sử dụng làm gốc.

Nhưng phải làm gì với các biến? Đây là một tình huống:

  1. Vlà một biến, lưu trữ một con trỏ tới đối tượng A.
  2. Thread 1đọc Vvà đình chỉ.
  3. Thread 2sửa đổi Vvà làm cho nó trỏ đến đối tượng B.
  4. Trình thu gom rác chạy giai đoạn "đánh dấu" của nó và các cuộc gặp gỡ Akhông còn được tham chiếu, sau đó giải phóng nó trong giai đoạn "quét".
  5. Thread 1đánh thức và cố gắng sử dụng A(đã đọc từ Vbước 2) bằng cách đánh dấu nó là root. Và thất bại , vì Akhông còn tồn tại.

Vậy, làm thế nào để xử lý việc này?

Thread 2thể đánh dấu đối tượng được thay thế Abằng cờ không loại bỏ đặc biệt (cờ tương tự được sử dụng cho các đối tượng mới được phân bổ). Nhưng khi nào nên gỡ cờ này? Tất nhiên Thread 1có thể làm điều đó. Nhưng Thread 2không biết gì về Thread 1, và do đó không thể chắc chắn rằng điều này sẽ được thực hiện bao giờ. Điều này có thể dẫn đến Asẽ không bao giờ được giải phóng. Và nếu GC sẽ xóa cờ đó, thì không có gì ngăn cản Ađược xóa khi GC chạy lần thứ hai ...

Các mô tả về trình thu gom rác quét và quét rác nhanh chóng mà tôi đã đọc chỉ đề cập rằng đối tượng được thay thế sẽ được "tô màu xám". Nhưng không có bất kỳ chi tiết cụ thể. Một liên kết đến một mô tả chi tiết hơn về giải pháp sẽ được đánh giá cao.

Câu trả lời:


4

Tùy thuộc vào các chi tiết chính xác của việc triển khai trình thu gom rác, đây có thể không phải là vấn đề trong bước 4. Ví dụ, trong bước 2, luồng 1 có lẽ đọc Vvào một thanh ghi. Trình thu gom rác có thể sẽ cần kiểm tra nội dung của các thanh ghi cho tất cả các luồng đang hoạt động (đang chạy và bị treo) để xem liệu có tham chiếu đến bất kỳ đối tượng nào được giữ trong các thanh ghi không.

Chắc chắn, việc triển khai bộ thu gom rác được kết hợp chặt chẽ với môi trường vận hành (và phân luồng) mà nó chạy. Có nhiều kỹ thuật thực hiện để đảm bảo rằng tất cả các tham chiếu được lưu trữ và tạm thời được xem xét.


Nhưng có cách nào để làm điều đó theo cách độc lập với nền tảng không? Thanh ghi có thể chứa dữ liệu, trông giống như một con trỏ, nhưng thực tế thì không phải vậy. Việc triển khai GC của tôi là chính xác và dựa trên thực tế là bất kỳ con trỏ nào nó xử lý đều trỏ đến một khối dữ liệu có cấu trúc nhất định.
điệp khúc

Hmm, nhưng đây là một ý tưởng! Tôi có thể đặt con trỏ như vậy đến một số vị trí đã biết trong ngăn xếp (hoặc biến cục bộ-luồng) và làm cho GC kiểm tra nó.
điệp khúc

@lorus - đối với nhiều trình thu gom rác, các bảng được tạo bởi trình biên dịch cho GC biết các thanh ghi chứa con trỏ tại bất kỳ điểm đã cho nào trong một phương thức. Đối với những người khác, GC chỉ chạy ở "điểm an toàn" mà hoàn toàn vô hiệu hóa nội dung đăng ký. Ngoài ra, việc triển khai GC vốn phụ thuộc vào nền tảng ở một số cấp độ.
Stephen C

@StephenC Chà, tôi không nghĩ thuật toán có mục đích chung như bộ sưu tập rác đòi hỏi phải phụ thuộc vào nền tảng như vậy. Hoạt động nguyên tử và rào cản bộ nhớ - có. Nhưng truy cập trực tiếp vào sổ đăng ký? Không, tôi không nghĩ vậy. Nó sẽ hiệu quả, nhưng tôi tin rằng nó không hoàn toàn bắt buộc. Tôi muốn biết thêm về các thuật toán độc lập với nền tảng như vậy.
điệp khúc

0

Bạn phải đánh dấu các biến cục bộ đôi khi trong giai đoạn đánh dấu. Tất cả các biến cục bộ, bao gồm cả các biến thường sống trên stack. Bằng cách nào đó.

Tôi cũng nghĩ rằng nó phải được thực hiện trong giai đoạn đồng bộ (tất cả các trình biến đổi đã dừng) để quét các đối tượng đã sửa đổi. Trong thực tế, cùng một vấn đề có thể phát sinh ngay cả khi không xem xét các biến / thanh ghi cục bộ. Xem xét đối tượng A trỏ đến null và đối tượng B trỏ đến C. Bây giờ bạn quét đối tượng A, một luồng biến đổi xuất hiện, sao chép tham chiếu đến C từ B sang A, bỏ qua B. Và bây giờ bạn có xung quanh để quét B. Và C bị trượt dưới ngón tay của bạn.

Tôi không biết về bất kỳ cách nào để đối phó với điều này sẽ không liên quan đến việc ngăn chặn những kẻ gây đột biến. Kỹ thuật thông thường là ở cuối giai đoạn đánh dấu để ngăn chặn tất cả các bộ biến đổi và đánh dấu lại tất cả các đối tượng mà chúng bị đột biến trong giai đoạn đánh dấu chính. Và bao gồm ngăn xếp và đăng ký trong đó.

Các thanh ghi đánh dấu thường được làm việc xung quanh bằng cách thực hiện đồng bộ bằng cách gọi bộ thu trong luồng đôi khi. Bên trong hàm collector, chỉ có các biến cục bộ của riêng nó (không phải là gốc) có thể nằm trong các thanh ghi, tất cả các biến cục bộ khác trong chuỗi cuộc gọi đều nằm trên ngăn xếp, vì vậy bạn có thể đi bộ ngăn xếp.

Ngoài ra, bạn có thể gửi tín hiệu đến chủ đề. Trình xử lý tín hiệu sẽ một lần nữa buộc tất cả các biến trên ngăn xếp, vì vậy bạn có thể đi bộ chúng. Nhược điểm của phương pháp này là nó phụ thuộc vào nền tảng.


Đúng. Nhưng tình huống a đang nói không phải là về một biến cục bộ. Nó là về một toàn cầu, có thể được truy cập và sửa đổi đồng thời bởi các chủ đề khác nhau.
điệp khúc

nếu sau khi thread1 đã đọc V và A không có trong biến cục bộ của thread1 thì A sẽ không thể truy cập được sau khi thread2 sửa đổi V, vì vậy nó sẽ sẵn sàng để thu thập
ratchet freak

@lorus: Nếu một luồng tải một cái gì đó vào thanh ghi, thì thanh ghi là biến cục bộ. Cho dù nó đã được đề cập trong nguồn cấp cao hoặc được thêm bởi trình biên dịch không quan trọng. Bạn chỉ coi nó là biến cục bộ.
Jan Hudec

@JanHudec Ok. Giờ thì tôi đã hiểu. Vì vậy, việc gọi trình thu thập trong luồng (máy khách) thực sự là một loại khóa toàn cầu?
điệp khúc

1
@JanHudec trừ khi các luồng hoạt động với GC và có thể thêm các đối tượng vào hàng đợi đánh dấu, mỗi khi một luồng đọc một tham chiếu
ratchet freak
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.