Bất cứ khi nào bạn chạy mã của mình, bạn sử dụng bộ nhớ. Khi bạn viết bằng một ngôn ngữ như Ruby, có vẻ như bộ nhớ dành cho bạn là vô hạn. Bạn có thể tiếp tục mà không cần suy nghĩ về dung lượng bộ nhớ cố định mà hệ thống đang chạy mã của bạn có. Trong tập Ruby Magic này, chúng tôi sẽ giải thích cách thức hoạt động của nó!
Một chút lịch sử
Ngày trước, các ngôn ngữ kịch bản như Ruby vẫn chưa tồn tại. Người ta chỉ viết mã bằng các ngôn ngữ như C, một ngôn ngữ lập trình cấp thấp. Một trong những điều khiến các ngôn ngữ này ở mức thấp là bạn phải tự dọn dẹp sau. Ví dụ:bất cứ khi nào bạn cấp phát bộ nhớ để lưu trữ String
, bạn cũng phải quyết định thời điểm dọn dẹp nó.
Dọn dẹp thủ công
Nó trông giống như đoạn mã Ruby giả sau đây. Nó khai báo một biến và sử dụng phương thức free
– Phương thức này không thực sự tồn tại trong Ruby– để dọn dẹp bộ nhớ mà chúng tôi đã sử dụng sau khi thực hiện xong với biến.
1_000_000.times do |i|
variable = "Variable #{i}"
puts variable
free(variable)
end
Một cách lập trình tẻ nhạt
Bạn có thể đã nhận ra rằng có một rủi ro ở đây:điều gì sẽ xảy ra nếu bạn quên free
biến? Trong trường hợp đó, nội dung của biến đó sẽ chỉ tồn tại trong bộ nhớ cho đến khi quá trình thoát ra. Nếu bạn làm điều này đủ thường xuyên, bạn sẽ hết bộ nhớ và quá trình của bạn bị treo.
Ví dụ tiếp theo minh họa một vấn đề phổ biến khác:
1_000_000.times do |i|
variable = "Variable #{i}"
free(variable)
puts variable
end
Chúng tôi khai báo biến và free
nó. Nhưng sau đó chúng tôi cố gắng sử dụng nó một lần nữa, điều này là không thể vì nó không tồn tại nữa. Nếu đây là C, chương trình của bạn bây giờ sẽ gặp sự cố với segfault
. Rất tiếc!
Con người là cỗ máy lầm lỗi
Con người nổi tiếng là tệ khi không phạm phải những sai lầm kiểu này mọi lúc. Do đó cần phải có một cách tự động dọn dẹp bộ nhớ. Cách phổ biến nhất để làm điều này –cũng được sử dụng trong Ruby– là Thu gom rác (GC).
Cách thức hoạt động của Bộ sưu tập rác (GC)
Bằng ngôn ngữ sử dụng GC, bạn có thể tạo các đối tượng mà không cần dọn dẹp chúng theo cách thủ công. Bất cứ khi nào bạn tạo một đối tượng, nó được đăng ký với Bộ thu gom rác. GC cố gắng theo dõi tất cả các tham chiếu mà bạn thực hiện cho đối tượng này. Khi nó xác định bạn không sử dụng đối tượng nữa, nó sẽ được đánh dấu để dọn dẹp. Thỉnh thoảng, Trình thu gom rác tạm dừng chương trình của bạn và dọn dẹp tất cả các đối tượng được đánh dấu.
Xem một số ví dụ
Trong vòng lặp đơn giản mà chúng tôi đã sử dụng trước đó, công việc của GC khá dễ dàng. Với mỗi lần lặp lại của vòng lặp, biến không còn được sử dụng ở bất kỳ đâu nữa. Biến ngay lập tức có thể được đánh dấu để dọn dẹp.
1_000_000.times do |i|
variable = "Variable #{i}"
puts variable
end
Trong ví dụ tiếp theo, chúng tôi chuyển biến vào puts_later
phương thức chờ trong 30 giây và sau đó puts
biến.
def puts_later(variable)
Thread.new do
sleep 30
puts variable
end
end
1_000_000.times do |i|
variable = "Variable #{i}"
puts_later variable
end
Công việc của Người thu gom rác đã khá phức tạp trong ví dụ tương đối đơn giản này. Nó phải hiểu rằng chúng tôi tham chiếu biến trong puts_later
phương pháp. Bởi vì phương thức bắt đầu một chuỗi, Bộ thu gom rác phải theo dõi chuỗi và đợi nó kết thúc. Chỉ khi đó, biến mới có thể được đánh dấu để dọn dẹp.
Khi nó trở nên phức tạp
Nếu không đi sâu vào các ví dụ phức tạp, hãy tin tôi khi tôi nói rằng công việc của Người thu gom rác thực sự rất khó. Điều này cũng giải thích tại sao GC có thể gây ra chi phí và các vấn đề trong môi trường sản xuất của bạn. Nó cần phải có một sự hiểu biết rất chi tiết về những gì đang xảy ra trong chương trình của bạn để xóa bộ nhớ đúng cách, điều này cần khá nhiều chu kỳ CPU để hoạt động đúng. Nhưng này, nó đánh bại bạn khi tự dọn dẹp!
Còn nhiều thứ nữa để Thu gom rác
Đây chỉ là phần giới thiệu của chúng tôi về Bộ sưu tập rác. Trong một bài viết tới, chúng ta sẽ xem xét cách thức hoạt động chính xác của tính năng này trong Ruby và cách bạn có thể đo lường và điều chỉnh GC để cải thiện hiệu suất của ứng dụng của mình.
Cập nhật: Tập tiếp theo có tại đây.