Đây là một tiến trình đặc biệt có nhiệm vụ duyệt qua các vùng nhớ đã được cấp phát và kiểm tra xem vùng nhớ nào không còn được sử dụng nữa không còn tham chiếu tới nó nữa thì sẽ thực hiệ
Trang 1TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG
BÀI TẬP LỚN
Môn: NGUYÊN LÝ CÁC NGÔN NGỮ LẬP TRÌNH
Giáo viên hướng dẫn: TS Nguyễn Hữu Đức
Học viên thực hiện: Nguyễn Văn Chiến CB121349
Trang 2MỤC LỤC
1 Tổng Quan Về Dọn Rác Nhớ (Garbage Collection) 2
1.1 Khái niệm Garbage Collection 2
1.2 Chức năng Garbage Collection 2
1.3 Ưu điểm của Garbage Collection 2
1.4 Nhược điểm của Garbage Collection 3
2 Các Kỹ Thuật Dọn Rác Nhớ (Garbage Collection Techniques) 4
2.1 Reference Counting collection 4
2.2 Mark-Sweep collection 5
2.3 Mark-Compact collection 7
2.4 Copying collection 11
2.5 Non-Copy Implicit collection 13
3 Kết Luận 14
4 Tài Liệu Tham Khảo 15
Trang 31 Tổng Quan Về Dọn Rác Nhớ (Garbage Collection)
Trong NET (cũng như Java) có một khái niệm là Bộ dọn rác (Garbage Collector) Đây là một tiến trình đặc biệt có nhiệm vụ duyệt qua các vùng nhớ
đã được cấp phát và kiểm tra xem vùng nhớ nào không còn được sử dụng nữa (không còn tham chiếu tới nó nữa) thì sẽ thực hiện thu hồi một cách tự động để
có thể cấp phát cho các yêu cầu tiếp theo
Việc xây dựng một game hay ứng dụng mà không quan tâm đến loại bỏ các đối tượng không còn cần thiết sẽ gây ra một số vấn đề rất lớn, chương trình
sẽ ngốn tài nguyên của hệ thống (Ram, CPU), dẫn đến việc chương trình sẽ chạy chậm chạp, nặng nề, gây treo hoặc thậm chí là bắt người dùng phải khởi động lại máy tính
1.1 Khái niệm Garbage Collection
Garbage Collection (GC) là một quá trình chạy ngầm để giải phóng vùng nhớ được cấp phát cho các đối tượng mà ứng dụng không còn sử dụng các đối tượng này nữa
1.2 Chức năng Garbage Collection
- Tự động khôi phục bộ nhớ;
- Chức năng của GC là tìm những đối tượng dữ liệu không còn sử dụng nữa và giải phóng chúng bởi chương trình đang chạy
Dưới đây là ví dụ về hoạt động của Garbage Collection
1 var a:Object = {foo:"bar"} // tạo object foo có giá trị là bar, sau đó đặt
1 tham chiếu a:
2 var b:Object = a; // sao chép tham chiếu sang b
3 delete(a); // xóa tham chiếu a
4 trace(b.foo); // khi thực hiện trace, kết quả là "bar" -> kết luận là object foo vẫn tồn tại
Như vậy vẫn còn 1 tham chiếu đến object foo, và để GC được thực hiện,
ta phải đảm bảo không còn tham chiếu nào đến foo nữa
5 delete(b);
Sau khi thực hiện dòng lệnh 5, foo sẽ không còn tham chiếu nào nên lúc này GC sẽ giải phóng foo
Trang 41.3 Ưu điểm của Garbage Collection
- Giúp lập trình viên không phải xử lý giải phóng bộ nhớ khi lập trình (vốn gây ra lỗi trên các ngôn ngữ không sử dụng Garbage Collector), giảm thiểu Memory leaks
- Bảo đảm tính toàn vẹn của chương trình, GC cũng là một phần quan trọng trong khả năng bảo mật và tính an toàn
1.4 Nhược điểm của Garbage Collection :
- Ảnh hưởng tới hiệu năng chương trình do JVM phải theo dõi các đối tượng khi đang được tham chiếu trong quá trình thực thi mã, sau đó phải finalize và giải phóng các đối tượng
Trang 52 Các Kỹ Thuật Dọn Rác Nhớ (Garbage Collection Techniques)
2.1 Reference Counting Collection
Reference Counting là cách đơn giản nhất để theo dõi xem đối tượng còn tham chiếu nào đang hoạt động hay không
Mỗi đối tượng kết hợp với 1 biến đếm số tham chiếu đến nó Mỗi khi có
1 tham chiếu đến đối tượng được tạo, biến đếm tham chiếu sẽ tăng lên 1, nếu tổng số tham chiếu = 0 thì Garbage Collector hoạt động và đối tượng sẽ bị xóa
Hiệu quả của RC.
Khi một con trỏ được tạo, biến đếm tham chiếu của đối tượng mà nó trỏ đến phải được điều chỉnh Nếu giá trị của biến con trỏ được chuyển từ con trỏ này đến con trỏ khác(phép gán), thì hai biến RC của cả hai con trỏ phải được cập nhật, một biến RC của 1 đối tượng sẽ tăng còn biến kia sẽ giảm Sau đó phải kiểm tra RC=0 hay không Những biến ngăn xếp mà có thời gian sống ngắn
sẽ phải chịu chi phí lớn cho mô hình RC Trong trường hợp này biến đếm tham chiếu được tăng lên và giảm trở lại nhanh chóng
Phần lớn chi phí có thể tối ưu bằng cách sử dụng biến cục bộ Tham chiếu từ biến cục bộ không cần giữ lại Chúng ta chỉ cần điều chỉnh RC trong heap Tuy nhiên chúng ta không thể bỏ qua hoàn toàn các con trỏ trong stack
Vì vậy stack được quét trước khi đối tượng được giải phóng và chỉ khi RC của con trỏ=0 thì nó được giải phóng Hạn chế cập nhật RC của đối với con trỏ trong stack sẽ giảm đáng kể chi phí của RC
Recap RC thì không phù hợp với những ứng dụng cần performance cao Khá phổ biến trong những ứng dụng không sử dụng cấu trúc vòng Hầu hết file
hệ thống sử dụng RC to quản lý file hoặc disk blocks
Kỹ thuật Reference Counting là cách phổ biến nhất nhưng nó vẫn có một
số nhược điểm, và có một số trường hợp dù ta đã xóa đối tượng nhưng số tham chiếu đến đối tượng đó vẫn lớn hơn 1 và Garbage Collector sẽ không bao giờ hoạt động
Ví dụ dưới đây 2 đối tượng sẽ tham chiếu chéo lẫn nhau và chúng sẽ không bao giờ bị xóa thật sự
Trang 6var a:Object = {}
var b:Object = {foo:a};
a.foo = b;
delete(a);
delete(b);
Reference Counting Collection là một phương pháp đơn giản và không chiếm tài nguyên CPU, tuy nhiên phương pháp này không thể áp dụng cho các tham chiếu vòng (Circular Reference) như ví dụ dưới đây
1 var a:Object = {}
2 var b:Object = {foo:a}; // tạo một tham chiếu b thứ hai - trỏ đến tham chiếu thứ nhất a
3 a.foo = b; // cho tham chiếu a trỏ vào b
4.
5 delete(a); // xóa tham chiếu a
6 delete(b); // xóa tham chiếu b
Trong trường hợp trên, dù cả a và b đều đã bị xóa và chẳng còn cách nào truy cập được 2 objects trên, nhưng số lượng tham chiếu vẫn là 1 vì chúng tham chiếu qua lại lẫn nhau Trường hợp này sẽ phức tạp hơn nếu ta có 1 danh sách vòng ( ví dụ: a->b, b->c, c->a …)
RC thất bại khi giải phóng cấu trúc vòng nguyên nhân từ việc xác định rác Cấu trúc vòng không hiếm trong các chương trình ngày nay: Cây; Cấu trúc
dữ liệu vòng
2.2 Mark-Sweep collection
Kỹ thuật Mark-Sweep giải quyết vấn đề cấu trúc vòng của RC
Phân biệt đối tượng sống từ rác ( Mark phase )
- Thực hiện bằng đánh dấu: Nó bắt đầu tại nút gốc root set và duyệt qua
đồ thị các con trỏ có thể đến được
- Đánh dấu những đối tượng đi qua
Trang 7Thu hồi vùng nhớ rác ( Sweep phase).
Sau Mark phase, bộ nhớ sẽ được kiểm tra kỹ lưỡng để tìm ra những đối tượng không được đánh dấu (rác) và thu hồi chúng
Dùng 1 bit làm mark-bit
1 : đánh dấu; 0 : không đánh dấu
Ví dụ:
Hình 1.
Trang 8Hình 2.
Thuật toán cơ bản:
New(A)=
If free_list is empty
mark_sweep()
if free_list is empty
(“out-of-memory”)
pointer = allocate(A)
return (pointer)
Mark(Obj)=
If mark_bit(Obj) == unmarked for C in Children(Obj)
mark(C)
mark_sweep()=
for Ptr in Roots mark(Ptr) sweep()
sweep()=
p = Heap_bottom while (p<Heap_top) if(mark_bit(p) == unmarked) then free(p)
else mark_bit(p) = unmarked: p=p+size(p)
Tính chất của Mark - Sweet:
- Về việc phân mảnh: Khó cấp phát những đối tượng lớn Vài đối tượng nhỏ có thể lấy nhiều khoảng trống kế tiếp
- Về chi phí: Tỉ lệ với kích thước heap, gồm cả đối tượng sống và rác
- Về Locality of reference: Không di chuyển đối tượng Đối tượng được đặt lẫn lộn là nguyên nhân nhiều page swaps (thông thường thì những đối tượng trong cluster thường được active cùng lúc)
2.3 Mark-Compact collection
Trang 9Kỹ thuật Mark-Compact giải quyết vấn đề phân mảnh và cấp phát của
Mark-Sweep
Bộ dọn rác sẽ duyệt qua đồ thị con trỏ và thực hiện sao chép từng đối tượng sống sau bước này
Kết quả là một vùng trống liên tục tiếp theo Đối tượng sống ở một phía
và rác ở cũng như vùng trống ở một phía
Những đối tượng rác được nén về cuối bộ nhớ
Tiến trình sẽ trải qua một số bước trên bộ nhớ:
- Một để tính vị trí mới của đối tượng (địa chỉ)
- Cập nhật con các con trỏ và di chuyển các đối tượng
- Thuật toán này chậm hơn đáng kể so với Mark-Sweep
2 thuật toán ví dụ về Mark-Compact:
- Two-finger Alg– cho những đối tượng có cùng kích thước
- Lisp 2 Alg
Thứ tự của đối tượng
Hình 3.
Arbitrary – thứ tự không được bảo vệ (tùy ý)
Linearizing – đối tượng trỏ đến đối tượng khác được di chuyển về vị trí
kề (tuyến tính)
Trang 10Sliding –các đối tượng được trượt về cuối heap giữ nguyên thứ tự cấp phát
Thuật toán Two Finger [Edwards 1974]
Thuật toán đơn giản:
- Thiết kế cho những đối tượng cùng kích thước
- Thứ tự của đối tượng sau thuật toán là tùy ý(arbitrary)
- 2 phần chính: nén và cập nhật các con trỏ
Phần I: Nén (Compact)
Sử dụng 2 con trỏ: con trỏ thứ nhất là free: Duyệt từ cuối heap để tìm những đối tượng rỗng (free/empty objects) Con trỏ thứ 2 là live: Duyệt từ đầu heap tìm những đối tượng sống (live objects) Khi con trỏ free tìm được một free spot và con trỏ live tìm được một đối tượng sống, đối tượng được di chuyển đến free spot Khi một đối tượng được di chuyển, con trỏ sẽ trỏ đến ô nhớ ở bên trái ô nhớ cũ
Dưới đây là ví dụ về two finger
Hình 4.
Phần II: Fix Pointers
Trang 11Duyệt qua các đối tượng sống trong heap.
- Duyệt thông qua các con trỏ
- Nếu con trỏ trỏ đến vùng khu vực rỗng, fix nó ứng với con trỏ chuyển tiếp đến đối tượng đích Compacted area free area
Hình 5.
Tính chất của Two finger:
- Đơn giản
- Tương đối nhanh: chỉ 2 lần duyệt
- Không đòi hỏi thêm khoảng trống
- Đối tượng phải có kích thước bằng nhau
- Thứ tự đối tượng output là tùy ý
- Điều này làm giảm hiệu quả chương trình! Do đó không được sử dụng ngày nay
Thuật toán Lisp2
- Cải tiến: Sử dụng cho những biến khác kích thước, giữ đúng thứ tự
- Đòi hỏi thêm pointer field cho mỗi đối tượng
Hình 6.
Thuật toán Lisp2
- Pass1: Tính toán địa chỉ Giữa địa chỉ mới trong object field mới được thêm vào
Trang 12- Pass2: Cập nhật con trỏ.
- Pass3: 2 con trỏ (free và live) chạy từ cuối Live objects được di chuyển đến free space giữ đúng thứ tự gốc
Hình 7.
Lisp 2 – Tính chất :
- Tương đối đơng giản
- Không phụ thuộc kích thước đối tượng
- Thứ tự các đối tượng được bảo vệ
- Chậm: 3 passes Cần thêm khoảng trống – một con trỏ trên một đối tượng
2.4 Copying collection
Kỹ thuật này giống kỹ thuật Mark-Compact, thuật toán di chuyển tất cả live objects vào một khu vực, phần còn lại của heap được giải phóng
Có một vài schemes cho copying GC, “Stop and-Copy” GC là một ví dụ
Stop-and-Copy Collector
Bộ nhớ heap được chia làm 2 phần
- Khi chương trình đang chạy yêu cầu cấp phát mà không còn đủ vùng nhớ chưa sử dụng
- Chương trình sẽ dừng và copying GC được gọi để thu hồi khoảng trống
Trang 13Hình 8.
Cheney breath-first copying
Hình 9.
Thuật toán:
Init()=
Tospace=Heap_bottom
space_size=Heap_size/2
top_of_space=Tospace+space_size
fromspace=top_of_space+1
free=Tospace
New(n)=
if free+n>top_of_space Collect()
if free+n>top_of_space abort”Memoryexhausted” new-object=free
free=free+n return(new-object)
Collect()=
from-space,to-space=
to-space,from-space//swap
scan=free=Tospace
Copy(P)=
ifforwarded(P) returnforwarding_address(P) else
Trang 14forRinRoots
R=copy(R)
whilescan<free
forPinchildren(scan)
*p=copy(P)
scan=scan+size(scan)
addr=free mem-copy(P,free) free=free+size(P) forwarding_address(P)=addr return(addr)
Efficiency of Copying Collection
-Thứ tự đối tượng là tùy ý
- Công việc hoàn thành phụ thuộc số lượng đối tượng sống
- Để giảm tần suất của GC, cần cấp phát semispaces lớn
- Không thực tế nếu không đủ RAM và paging xuất hiện
2.5 Non-Copy Implicit collection
Kỹ thuật này cần thêm 2 trường con trỏ và một trường màu cho mỗi đối tượng Những trường này phục vụ cho việc liên kết giữa các vùng nhớ trong một danh sách liên kết đôi Trường màu xác định đối tượng thuộc về tập live objects hay tập rác
Duyệt tất cả các đối tượng trong vùng nhớ heap Các đối tượng live object sẽ linking đến tập toset, và màu chuyển sang màu khác Sau khi di chuyển các live object từ fromset sang thì các object còn lại trong fromset là garbage và có thể sử dụng như một free list Sau đó tập hợp fromset sẽ hoán đổi thành toset (giống như fromspace và tospace trong copying collector)
Trong hầu hết trường hợp thì chi phí nhỏ hơn copying collector nhưng trong vài trường hợp thì chi phí phân mảnh có thể quá nặng
Trang 153 Kết Luận
Qua phần trình bày trên ta thấy, có 5 kỹ thuật dọn rác nhớ và mỗi kỹ thuật đều có những ưu nhược điểm khác nhau
Nhờ các kỹ thuật dọn rác nhớ mà các lập trình viên không phải xử lý giải phóng bộ nhớ khi lập trình (vốn gây ra lỗi trên các ngôn ngữ không sử dụng Garbage Collector), giảm thiểu Memory leaks Bảo đảm tính toàn vẹn của chương trình, GC cũng là một phần quan trọng trong khả năng bảo mật và tính
an toàn
Bên cạnh ưu điểm kể trên thì, garbage collection sẽ dẫn đến một số hạn chế như ảnh hưởng tới hiệu năng chương trình do JVM phải theo dõi các đối tượng khi đang được tham chiếu trong quá trình thực thi mã, sau đó phải finalize và giải phóng các đối tượng không còn tham chiếu trực tiếp
Trang 164 Tài liệu tham khảo.
[1] http://www.slideshare.net/khuonganpt/basic-garbage-collection-techniques-vi
[2] http://www.ibm.com/developerworks/vn/library/j-nativememory-linux/ index.html
[3] http://chienuit.wordpress.com/2010/10/04/qu%E1%BA%A3n-l-b%E1%BB
%99-nh%E1%BB%9B-trong-net-garbage-collection/
[4] http://cgi.di.uoa.gr/~smaragd/521-10/gc.pdf