Trang chủ > Uncategorized > Memory leak trong Java

Memory leak trong Java

Có hai lỗi quản lí bộ nhớ mà ai học C/C++ cũng từng mắc phải là memory leak và dangling pointer. Memory leak xảy ra khi chương trình không thể thu hồi (hay truy cập) một vùng nhớ đã cấp phát mặc dù không còn sử dụng. Nó xảy ra khi tất cả con trỏ trỏ đến vùng nhớ đó bị thay đổi giá trị trước khi vùng nhớ được giải phóng. Dangling pointer xảy ra khi tồn tại con trỏ trỏ tới một vùng nhớ chưa được cấp phát, hoặc đã bị thu hồi.

Khác với C/C++, việc thu hồi vùng nhớ trong Java được thực hiện bởi Gabage Collector (GC). Các vùng nhớ không còn được trỏ tới sẽ có khả năng bị thu hồi (mặc dù ta không thể đảm bảo được thời điểm mà GC sẽ thực hiện điều đó) (không còn memory leak). Và tất nhiên, các vùng nhớ vẫn còn được trỏ tới bởi ít nhất một reference sẽ không bị thu hồi (không còn dangling pointer).

Vùng nhớ chỉ thỏa mãn tiêu chuẩn thu hồi khi không còn reference nào trỏ tới. Điều này nghĩa là ta phải nhớ xóa tất cả các reference tới vùng nhớ không còn dùng. Và thông thường, chuyện này không có gì phức tạp, chỉ cần trỏ reference tới vùng nhớ khác, tới null, hoặc để reference tự biến mất khi ra khỏi block chứa nó (chẳng hạn, khi method khai báo reference đó kết thúc). Nói chung là đa số được thực hiện một cách “vô tình”, tức là ta thậm chí không để ý là mình đang làm điều đó.

Nhưng có những tình huống mà chúng ta phải chủ động xóa reference, ví dụ như khi làm việc với Listener. Để A có thể nhận thông báo về các event mới trong B, nó phải được thêm vào danh sách Listener của B, giả sử bằng method addActionListener. Khi muốn kết thúc sự tồn tại của A, ta cần để đưa A ra khỏi danh sách, giả sử bằng removeActionListener. Đây là điều bắt buộc khi chúng ta muốn vùng nhớ của A có thể được thu hồi, vì chừng nào A còn nằm trong danh sách Listener thì vẫn còn reference trỏ đến nó. Tất nhiên, triển vọng là ta sẽ quên làm việc đó, như cái cách chúng ta quên giải phóng bộ nhớ trong C/C++. Chưa kể đến các tình huống đau đầu như có vài vị trí (ví dụ như vài nhánh if) có thể kết thúc A, đồng nghĩa với việc có vài vị trí cần gọi removeActionListener😉.

Như vậy, mặc dù phần mềm viết bằng Java không có kiểu memory leak như C/C++, nhưng vẫn có thể có những vùng nhớ không còn sử dụng, song cũng không thể bị thu hồi, đó chính là memory leak “kiểu Java”.

Chính vì lí do trên, Java cho chúng ta thêm một số dạng reference đặc biệt trong package java.lang.ref, là WeakReference (WR) và SoftReference (SR). Các reference này cũng được dùng để trỏ tới các vùng nhớ, như reference mà chúng ta vẫn quen dùng (giờ gọi là strong reference). Tuy nhiên, nếu vùng nhớ được trỏ tới bởi cả strong reference lẫn WR, thì khi strong reference cuối cùng bị xóa bỏ, vùng nhớ này có thể được thu hồi bởi GC, và WR không có khả năng ngăn cản điều đó. SR “strong” hơn WR một chút, nghĩa là vùng nhớ được trỏ tới bởi WR sẽ được GC ưu tiên thu hồi hơn (ưu tiên tới đâu thì phụ thuộc vào hiện thực của từng máy ảo Java). Bây giờ, nếu ta đưa A vào danh sách Listener qua một WR chứ không phải là strong reference, ta sẽ không cần bận tâm tới việc gọi removeActionListener khi muốn kết thúc A nữa. Chi tiết cách sử dụng WR và SR, tham khảo JDK documentation và Thinking in Java (ví dụ khá rõ, dù không nói nhiều về ý nghĩa của WR và SR).

Chú ý là với các ứng dụng thông thường, thì hiện tượng này nếu có cũng không làm máy cạn bộ nhớ được, vì vậy cũng không nhất thiết phải dùng đến SR và WR.

Tags:
  1. Chưa có phản hồi.
  1. No trackbacks yet.

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s

%d bloggers like this: