Leak memory trong Android (Phần 1)
Bài đăng này đã không được cập nhật trong 3 năm
Xin chào mọi người !!!!
Bài viết này mình xin chia sẻ về một vấn đề rất thường gặp trong lập trình Android nói riêng và Java nói chung là Leak Memory
.
Vậy Leak Memory
là gì?
Hãy dạo qua sơ lược về khái niệm Leak Memory
là gì nhé !!
Hãy bất đầu với sự so sánh cơ chế quản lý bộ nhớ giữa ngôn ngữ lập trình C
và ngôn ngữ lập trình Java
.
Trong C
, khi một lập trình viên khai báo một biến thì phải tự phân bổ kích thước memory
được sử dụng và phải tự giải phóng memory
thủ công sau khi sử dụng. Đối với Java
thì khác, khi bạn khai báo một kiểu new Interger(1)
, bạn không cần phải cấp phát bộ nhớ cho nó, đó là công việc được xử lý bởi Java Virtual Machine (JVM)
.
Sau đó, trong vòng đời của ứng dụng, JVM
sẽ thường xuyên kiểm tra, xác định rằng object
nào trong memory
đang sử dụng hoặc không sử dụng và những object
nào không sử dụng sẽ bị thu hồi lại tài nguyên đã cấp phát cho nó. Quá trình này được gọi là thu góp rác hay còn được gọi là Garbage Collector (GC)
.
Quá trình quản lý tự động bộ nhớ của Java hoàn toàn dựa trên GC
- quá trình kiểm tra phát hiện những object nào không còn được sử dụng và lấy lại tài nguyên đã cấp phát của nó.
Và như thế, bạn có thể hiểu nôm na Memory Leak
có nghĩa là GC
sẽ không thể thu hồi lại những vùng memory
đã cấp phát cho một object unused
( không còn được sử dụng ).
Mỗi ứng dụng sẽ được cấp phát một lượng memory
nhất đinh, và những object unused
nhưng không được thu hồi bộ nhớ dần dần sẽ tăng lên dẫn tới tràn bộ nhớ.
Vì vậy một ngày đẹp trời, ứng dụng của bạn có thể quăng ra lỗi OutOfMemoryException
bất cứ lúc nào.
OutOfMemoryException
theo mình là một lỗi cực kỳ nguy hiểm, khi nó sẽ dẫn tới việc ứng dụng của bạn crash và buộc phải khởi động lại. Lỗi này có thể tránh được nếu chúng ta hiểu rõ và nắm chắc về việc quản lý bộ nhớ trong vòng đời của ứng dụng, điều mà chúng ta sẽ cùng nhau tìm hiểu trong các loạt bài về Leak Memory
.
Vậy chúng ta đã tạm hiểu rõ Leak Memory
là gì rồi đúng không nào? Bước tiếp theo chúng ta sẽ cùng nhau tìm hiểu về Garbage Collector (GC)
.
Garbage Collector (GC)
giúp Java
quản lý memory
ra sao và nó hoạt động như thế nào?
Garbage Collector (GC)
Rất nhiều lập trình viên nghĩ rằng, Garbage Collector
thu nhập và loại bỏ những object unused (dead object)
. Nhưng trong thực tế, điều đó hoàn toàn ngược lại.
Những object
đang được sử dụng (live object
) sẽ được track (theo dõi) bởi GC
, và những object
nằm ngoài sự theo dõi của GC
được coi là những dead object
hay còn gọi là object rác
(cần được thu hồi tài nguyên). Sự nhầm lẫn trên có thể dẫn tới rất nhiều vấn đề về hiệu năng (performance
) của ứng dụng.
Một sự nhầm lẫn khác nữa về vấn đề cấp phát và thu hồi bộ nhớ của GC
. Nhiều lập trình viên nghĩ rằng GC
sẽ thu hồi memory
của những dead object
và trả lại memory
về cho hệ điều hành. Điều này thực chất chưa đúng. Vì sao? Hãy nói một chút về bộ nhớ Heap
và Stack
trong Java.
Java Heap và Stack Memory
Java Heap
và Stack Memory
là một phần của bộ nhớ được JVM
sử dụng để chạy chương trình Java
của bạn.
Khi bạn chạy chương trình Java
, JVM
sẽ yêu cầu hệ điều hành cấp cho một không gian bộ nhớ trong RAM
để dùng cho việc chạy chương trình. JVM
sẽ chia bộ nhớ được cấp phát này thành 2 phần: Heap
và Stack
cho việc quản lý.
Phương pháp lý bộ nhớ của Heap
và Stack Memory
là hoàn toàn khác nhau. Bộ nhớ Stack
tồn tại trong thời gian ngắn, nhưng bộ nhớ Heap
tồn tại từ lúc ứng dụng bắt đầu thực thi đến lúc kết thúc
Đối với bộ nhớ Stack
, các vùng nhớ của những biến dữ liệu nguyên thủy trong Stack
sẽ được tự động giải phóng sau khi khi gọi hàm.
Bất cứ khi nào gọi 1 hàm, một khối bộ nhớ mới sẽ được tạo trong Stack
cho hàm đó để lưu các biến local. Khi hàm thực hiện xong, khối bộ nhớ cho hàm sẽ bị xoá, và giải phóng bộ nhớ trong Stack
.
Đối với bộ nhớ Heap
thì hoàn toàn khác, Bộ nhớ Heap
trong Java
được dùng để cấp phát bộ nhớ cho các đối tượng, các lớp JRE
lúc thực thi. Bất cứ khi nào, chúng ta tạo đối tượng, nó sẽ được tạo trong bộ nhớ Heap
. Với những đối tượng không còn được tham chiếu nữa thì trình thu thập rác (Garbage Collection
) sẽ giải phóng bộ nhớ mà các đối tượng đó sử dụng. Vậy những vùng nhớ được giải phóng có được trả lại cho hệ điều hành hay không? Câu trả lời là không.
Mỗi khi ứng dụng thực thi, nó sẽ được cấp phát một vùng nhớ cố định. Giả sử vùng nhớ được chia thành 6 phần nhỏ, khi một object
mới được taọ ra, nó sẽ chiếm lấy 1 phần, vậy số vùng nhớ còn lại là 5. Sau đó, object
đó được giải phóng, thì vùng nhớ nó được cấp phát sẽ trả lại cho bộ nhớ heap
(6 phần) chứ không phải trả lại cho hệ điều hành. Quá trình này cơ bản là lấy lại vùng nhớ để sẵn sàng cung cấp cho những object
khác, chứ không phải lấy lại vùng nhớ cho hệ điều hành.
Vậy làm sao để biết application bị leak memory?
Câu trả lời là làm nhiều, chết nhiều thì biết thôi ). Ngoài ra còn có rất nhiều phần mềm giúp hỗ trợ phát hiện leak memory
.
Giả sử phần mềm của bạn đã gần tới lúc release thì sẽ rất tốn công nếu đi mò mẫm từng class, từng dòng code để phát hiện leak memory. Vậy nên bạn có thể sử dụng phần mềm hỗ trợ giúp bạn nhanh chóng phát hiện chỗ nào bị memory leak
mà giết nó trước khi đưa cho những tester sát thủ
Đối với Android
, mình xin giới thiệu các bạn một thư viện phát hiện Leak Memory
cực kỳ hiệu quả đó là LeakCanary.
Bạn chỉ việc cài đặt và cấu hình, xong chạy app, play around vài vòng, đi qua vài con đường activity
, thỉnh thoảng vọt lên notification
và nếu những chỗ nào có khả năng gây ra leak memory
thì Leak Canary
sẽ thông báo cho bạn.
Xong thì chúng ta bay vào sửa thôi, rất dễ dàng.
Tạm xong phần 1 nhé, phần sau chúng ta sẽ đi chuyên sâu về cách sử dụng Leak Canary
và một số trường hợp gây leak memory
kinh điển trong Java.
P/s: Bài viết có sử dụng tư liệu trên mạng (hình ảnh, kiến thức ...)
All rights reserved