Bạn nói đúng. Hình thức cụ thể của bộ sưu tập rác mà bạn mô tả được gọi là " đếm tham chiếu ". Cách thức hoạt động (về mặt khái niệm, ít nhất, hầu hết các triển khai hiện đại về đếm tham chiếu thực tế được thực hiện hoàn toàn khác nhau) trong trường hợp đơn giản nhất, trông như thế này:
- Bất cứ khi nào một tham chiếu đến một đối tượng được thêm vào (ví dụ: nó được gán cho một biến hoặc một trường, được truyền cho phương thức, v.v.), số tham chiếu của nó được tăng thêm 1
- Bất cứ khi nào một tham chiếu đến một đối tượng bị xóa (phương thức trả về, biến đi ra khỏi phạm vi, trường được gán lại cho một đối tượng khác hoặc đối tượng chứa trường được thu gom rác), số lượng tham chiếu giảm đi 1
- ngay khi số tham chiếu chạm 0, không còn tham chiếu đến đối tượng nữa, điều đó có nghĩa là không ai có thể sử dụng nó nữa, do đó nó là rác và có thể được thu thập
Và chiến lược đơn giản này có chính xác vấn đề bạn giải mã: nếu A tham chiếu B và B tham chiếu A, thì cả hai số tham chiếu của chúng không bao giờ có thể nhỏ hơn 1, có nghĩa là chúng sẽ không bao giờ được thu thập.
Có bốn cách để giải quyết vấn đề này:
- Bỏ mặc nó. Nếu bạn có đủ bộ nhớ, các chu kỳ của bạn nhỏ và không thường xuyên và thời gian chạy của bạn ngắn, có thể bạn có thể thoát khỏi chỉ với việc không thu thập các chu kỳ. Hãy nghĩ về một trình thông dịch kịch bản lệnh shell: các kịch bản shell thường chỉ chạy trong vài giây và không phân bổ nhiều bộ nhớ.
- Kết hợp bộ đếm rác tham chiếu của bạn với bộ thu gom rác khác không có vấn đề với chu kỳ. CPython thực hiện điều này, ví dụ: trình thu gom rác chính trong CPython là trình thu gom đếm tham chiếu, nhưng theo thời gian, một trình thu gom rác truy tìm được chạy để thu thập các chu kỳ.
- Phát hiện các chu kỳ. Thật không may, phát hiện các chu kỳ trong biểu đồ là một hoạt động khá tốn kém. Đặc biệt, nó đòi hỏi khá nhiều chi phí tương tự như một bộ sưu tập theo dõi, vì vậy bạn cũng có thể sử dụng một trong số đó.
- Đừng thực hiện thuật toán theo cách ngây thơ mà bạn và tôi sẽ làm: từ những năm 1970, đã có nhiều thuật toán khá thú vị được phát triển kết hợp phát hiện chu kỳ và đếm tham chiếu trong một thao tác theo cách thông minh rẻ hơn đáng kể so với thực hiện chúng cả hai cách riêng biệt hoặc làm một bộ sưu tập theo dõi.
Nhân tiện, cách chính khác để thực hiện một trình thu gom rác (và tôi đã gợi ý rằng một vài lần ở trên), là truy tìm . Một bộ sưu tập truy tìm dựa trên khái niệm khả năng tiếp cận . Bạn bắt đầu với một số tập hợp gốc mà bạn biết là luôn có thể truy cập (ví dụ: hằng toàn cục hoặc Object
lớp, phạm vi từ vựng hiện tại, khung ngăn xếp hiện tại) và từ đó bạn theo dõi tất cả các đối tượng có thể truy cập từ tập hợp gốc, sau đó tất cả các đối tượng có thể truy cập từ các đối tượng có thể truy cập từ tập gốc và cứ thế, cho đến khi bạn có bao đóng bắc cầu. Tất cả những gì không có trong đó là rác.
Vì một chu trình chỉ có thể truy cập trong chính nó, nhưng không thể truy cập được từ tập gốc, nên nó sẽ được thu thập.