TRƯỜ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
Lê Thị Trang
CB120118
Đố Tiến Dũng
CB121351
MỤ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
2
2
1. 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.
3
3
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.
1.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.
4
4
2. 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.
5
5
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ự.
var 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 )
6
6
- 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.
Thu 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.
7
7
Hình 2.
Thuật toán cơ bản:
New(A)=
Mark(Obj)=
If free_list is empty
If mark_bit(Obj) == unmarked
mark_sweep()
for C in Children(Obj)
if free_list is empty
mark(C)
return (“out-of-memory”)
pointer = allocate(A)
return (pointer)
mark_sweep()=
sweep()=
for Ptr in Roots
p = Heap_bottom
mark(Ptr)
while (p
sweep()
if(mark_bit(p) == unmarked)
then free(p)
else mark_bit(p) = unmarked:
p=p+size(p)
8
8
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
Kỹ 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
9
9
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).
Sliding –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
10
10
Hình 4.
Phần II: Fix Pointers
Duyệ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 ý.
11
11
- Đ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.
-
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 :
12
-
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ệ
12
-
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.
Hình 8.
Cheney breath-first copying
Hình 9.
Thuật toán:
13
13
Init()=
New(n)=
Tospace=Heap_bottom
if free+n>top_of_space
space_size=Heap_size/2
Collect()
top_of_space=Tospace+space_size
fromspace=top_of_space+1
if free+n>top_of_space
abort”Memoryexhausted”
free=Tospace
new-object=free
free=free+n
return(new-object)
Collect()=
Copy(P)=
from-space,to-space=
ifforwarded(P)
to-space,from-space//swap
returnforwarding_address(P)
scan=free=Tospace
else
top_of_space=Tospace+space_size
addr=free
forRinRoots
mem-copy(P,free)
R=copy(R)
free=free+size(P)
whilescan
forwarding_address(P)=addr
forPinchildren(scan)
return(addr)
*p=copy(P)
scan=scan+size(scan)
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.
14
14
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.
15
15
3. 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.
16
16
4. Tài liệu tham khảo.
[1] />[2] />[3] />%99-nh%E1%BB%9B-trong-net-garbage-collection/
[4] />
17
17