Để thêm vào cuộc tranh luận ở đây.
Có những vấn đề đã biết với bộ sưu tập rác và việc hiểu chúng giúp hiểu tại sao không có gì trong C ++.
1. Hiệu suất?
Khiếu nại đầu tiên thường là về hiệu suất, nhưng hầu hết mọi người không thực sự nhận ra những gì họ đang nói. Như được minh họa bởi Martin Beckett
vấn đề có thể không phải là hiệu suất mỗi lần, mà là khả năng dự đoán về hiệu suất.
Hiện tại có 2 họ của GC được triển khai rộng rãi:
- Mark-And-Sweep
- Loại đếm tham chiếu
Tốc Mark And Sweep
độ nhanh hơn (ít ảnh hưởng đến hiệu suất tổng thể) nhưng nó mắc phải hội chứng "đóng băng thế giới": tức là khi GC khởi động, mọi thứ khác sẽ dừng lại cho đến khi GC dọn dẹp. Nếu bạn muốn xây dựng một máy chủ trả lời trong vài mili giây ... một số giao dịch sẽ không đáp ứng mong đợi của bạn :)
Vấn đề Reference Counting
là khác nhau: đếm tham chiếu thêm chi phí, đặc biệt là trong môi trường Đa luồng vì bạn cần phải có số nguyên tử. Hơn nữa, có vấn đề về chu kỳ tham chiếu, do đó bạn cần một thuật toán thông minh để phát hiện các chu trình đó và loại bỏ chúng (thường được thực hiện bằng cách "đóng băng thế giới", mặc dù ít thường xuyên hơn). Nói chung, như ngày nay, loại này (mặc dù thông thường phản ứng nhanh hơn hoặc đúng hơn, đóng băng ít thường xuyên hơn) là chậm hơn so với Mark And Sweep
.
Tôi đã thấy một bài báo của những người thực hiện Eiffel đang cố gắng thực hiện Trình Reference Counting
thu gom rác có hiệu suất toàn cầu tương tự Mark And Sweep
mà không có khía cạnh "Đóng băng thế giới". Nó đòi hỏi một luồng riêng cho GC (điển hình). Thuật toán này hơi đáng sợ (cuối cùng), nhưng bài báo đã thực hiện tốt việc giới thiệu các khái niệm cùng một lúc và cho thấy sự tiến hóa của thuật toán từ phiên bản "đơn giản" sang phiên bản đầy đủ. Đề nghị đọc nếu chỉ tôi có thể đặt tay trở lại vào tệp PDF ...
2. Thu thập tài nguyên là khởi tạo (RAII)
Đó là một thành ngữ phổ biến ở C++
chỗ bạn sẽ bao bọc quyền sở hữu tài nguyên trong một đối tượng để đảm bảo rằng chúng được phát hành đúng. Nó chủ yếu được sử dụng cho bộ nhớ vì chúng tôi không có bộ sưu tập rác, nhưng dù sao nó cũng hữu ích cho nhiều tình huống khác:
- khóa (đa luồng, xử lý tệp, ...)
- kết nối (đến cơ sở dữ liệu, máy chủ khác, ...)
Ý tưởng là kiểm soát đúng thời gian tồn tại của đối tượng:
- nó sẽ sống miễn là bạn cần nó
- nó sẽ bị giết khi bạn hoàn thành nó
Vấn đề của GC là nếu nó giúp với cái trước và cuối cùng đảm bảo rằng sau này ... cái "tối thượng" này có thể không đủ. Nếu bạn phát hành một khóa, bạn thực sự muốn nó được phát hành ngay bây giờ, để nó không chặn bất kỳ cuộc gọi nào nữa!
Ngôn ngữ với GC có hai cách giải quyết:
- không sử dụng GC khi phân bổ ngăn xếp là đủ: thông thường đối với các vấn đề về hiệu năng, nhưng trong trường hợp của chúng tôi, nó thực sự có ích vì phạm vi xác định thời gian tồn tại
using
xây dựng ... nhưng RAII rõ ràng (yếu) trong khi ở C ++ RAII ẩn để người dùng KHÔNG THỂ vô tình mắc lỗi (bằng cách bỏ qua using
từ khóa)
3. Con trỏ thông minh
Con trỏ thông minh thường xuất hiện dưới dạng viên đạn bạc để xử lý bộ nhớ trong C++
. Thường thì tôi đã nghe nói: chúng ta không cần đến GC, vì chúng ta có con trỏ thông minh.
Người ta không thể sai nhiều hơn.
Con trỏ thông minh giúp ích: auto_ptr
và unique_ptr
sử dụng các khái niệm RAII, thực sự rất hữu ích. Chúng đơn giản đến mức bạn có thể tự viết chúng khá dễ dàng.
Tuy nhiên, khi một người cần chia sẻ quyền sở hữu sẽ khó khăn hơn: bạn có thể chia sẻ giữa nhiều luồng và có một vài vấn đề tinh tế với việc xử lý số đếm. Do đó, một cách tự nhiên đi về phía shared_ptr
.
Thật tuyệt, đó là thứ Boost cho, nhưng nó không phải là viên đạn bạc. Trong thực tế, vấn đề chính shared_ptr
là nó mô phỏng một GC được thực hiện bởi Reference Counting
nhưng bạn cần phải tự mình thực hiện phát hiện chu trình ... Urg
Tất nhiên là có điều này weak_ptr
, nhưng tôi không may đã thấy rò rỉ bộ nhớ mặc dù đã sử dụng shared_ptr
vì những chu kỳ đó ... và khi bạn ở trong môi trường Đa luồng, rất khó phát hiện!
4. Giải pháp là gì?
Không có viên đạn bạc, nhưng như mọi khi, nó chắc chắn khả thi. Trong trường hợp không có GC, người ta cần phải rõ ràng về quyền sở hữu:
- thích có một chủ sở hữu tại một thời điểm nhất định, nếu có thể
- nếu không, hãy chắc chắn rằng sơ đồ lớp của bạn không có bất kỳ chu kỳ nào liên quan đến quyền sở hữu và phá vỡ chúng bằng ứng dụng tinh tế của
weak_ptr
Vì vậy, thực sự sẽ rất tuyệt nếu có một GC ... tuy nhiên đó không phải là vấn đề nhỏ. Và trong lúc này, chúng ta chỉ cần xắn tay áo lên.