[Android Performance] Memory leak – Phần 1: Nguyên nhân và tác hại
Mình tin rằng nhiều người trong các bạn, thậm chí là các là những người lập trình lâu lăm cũng rất ít người biết về khái niệm Memory Leak, đó là bởi vì hiện tại không có nhiều lập trình viên quan tâm đến vấn đề Memory Leak. Tuy nhiên trong thực tế hiện nay rất nhiều phần mềm lại đang bị các vấn đề liên quan đến Resource Leak nói chung và Memory Leak nói riêng. Chính vì vậy mình muốn gửi tới các bạn bài viết này hi vọng để mọi người có được một cái nhìn tổng quát về Memory Leak và các tránh không để hiện tượng Memory Leak xảy ra.
I – Memory Leak là gì? Tác hại của nó là gì?
Memory Leak là hiện tượng mà chương trình quản lý bộ nhớ được cấp phát không chính xác dẫn đến việc một hoặc nhiều phần bộ nhớ không cần thiết hoặc không còn được sử dụng nhưng vẫn giữ lại và không bị giải phóng.
Đó là định nghĩa theo kinh nghiệm của mình, hoặc các bạn có thể tham khảo thêm về định nghĩa của Memory Leak tại trang wikipedia theo địa chỉ sau: https://en.wikipedia.org/wiki/Memory_leak.
Vậy Memory Leak có tác hại gì?
– Nhìn từ định nghĩa bạn đã có thể thấy ngay 1 tác hại của Memory Leak, đó chính là việc chương trình sẽ sử dụng quá nhiều bộ nhớ khi bị Memory Leak. Mà trong một hệ thống thông thường bộ nhớ luôn có giới hạn, vì vậy khi 1 chương trình sử dụng quá nhiều bộ nhớ thì các chương trình khác sẽ có nguy cơ phải sử dụng ít bộ nhớ hơn. Điều này dẫn tới 3 khả năng đó là các chương trình khác hoạt động với hiệu năng thấp hơn hoặc không thể hoạt động do thiếu bộ nhớ hoặc chương trình sử dụng quá nhiều tài nguyên sẽ bị “kill” bởi hệ thống. Và dù là khả năng nào xảy ra thì hiệu năng và trải nghiệm người dùng trên toàn bộ hệ thống đều bị sụt giảm.
– Đó là đối với hệ thống máy tính nói chung, còn đối với điện thoại Android thì sao? Dĩ nhiên là tác hại ở trên vẫn đúng trong trường hợp của điện thoại Android. Nhưng không dừng lại ở đó, hệ điều hành Android nói riêng và các hệ điều hành di động nói chung đều có cách quản lý bộ nhớ khác với các hệ điều hành cho máy tính để bàn hay laptop, chính vì vậy Memory Leak còn làm nảy sinh nhiều vấn đề khác. Cụ thể là trên điện thoại Android, khi ứng dụng của bạn sử dụng nhiều bộ nhớ GC sẽ phải hoạt động liên tục, hơn nữa cơ chế quản lý bộ nhớ của Android cũng thường xuyên phải hoạt động để giải phóng bộ nhớ của các app khác đồng thời với việc cấp phát bộ nhớ cho ứng dụng của bạn. Điều này dẫn tới hậu quả là chiếc điện thoại của bạn sẽ mau hết pin hơn do phải xử lý nhiều hơn, đồng thời với việc ứng dụng của bạn có thể bị giật lag, thậm chí là force close. Và tôi tin chắc là người dùng sẽ rất khó chịu và cũng sẽ gỡ bỏ ngay ứng dụng của bạn khi điều này xảy ra. Thật là tồi tệ phải không nào.
II – Tại sao lại xảy ra Memory Leak?
Tình trạng Memory Leak có thể xảy ra ở hầu hết các chương trình, trên hầu hết các ngôn ngữ lập trình nếu như người lập trình viên không có phương pháp tốt để quản lý bộ nhớ. Tuy nhiên phương pháp cấp phát và quản lý bộ nhớ lại phụ thuộc vào ngôn ngữ lập trình được sử dụng để tạo nên chương trình nên các phương pháp quản lý bộ nhớ cũng sẽ khác nhau cho từng ngôn ngữ. Do vậy, trong bài viết này, để hiểu rõ vấn đề Memory Leak đối với phần mêm Android, mình sẽ chỉ tập trung vào cơ chế của Java nói chung và Android nói riêng mà thôi.
Trước khi tìm hiểu về nguyên nhân dẫn tới Memory Leak, chúng ta cần tìm hiểu về cơ chế quản lý bộ nhớ mà cụ thể là Garbage Collection trong Java trước.
2.1. Cơ chế hoạt động của Garbage Collection
– Garbage Colletion (hay còn gọi là GC) là cơ chế thu dọn lại các vùng nhớ (memory) đã được cấp phát và không còn được sử dụng tới nữa. Điều này được thực hiện hoàn toàn tự động bới JVM (Java Virtual Machine).
– Grabage Collection hoạt động bằng việc theo dõi trạng thái của các vùng bộ nhớ đã cấp phát và khi vùng bộ nhớ này không còn được được sử dụng (được tham chiếu bởi các biến, các đối tượng) tại bất kì đâu trong chương trình thì nó sẽ được coi là Garbage và sẽ bị giải phóng để có thể tái sử dụng khi cần thiết.
– Cụ thể hơn, khi 1 vùng bộ nhớ được cấp phát động trên HEAP, tùy theo loại đối tượng mà chúng lưu trữ (như class object, static variable, …) chúng sẽ được nối vào cây object (object tree) tương ứng, và mỗi object tree sẽ có 1 gốc được gọi là GC Root, giống như hình minh họa bên dưới:
– Các object tree hợp lại thành GC Map, chứa toàn bộ các liên kết đến các đối tượng trong chương trình.
Thông qua phương pháp này, Garbage Collection có thể tạo được bản đồ liên kết giữa các đối tượng có trong ứng dụng.
– Và khi chương trình yêu cầu cấp phát bộ nhớ cho 1 đối tượng bằng toán tử new, chương trình sẽ kiểm tra xem vùng nhớ của chương trình có còn trống để cấp cho đối tượng này không. Nếu không còn hoặc khi dung lượng bộ nhớ của HEAP lớn hơn 1 ngưỡng nào đó đượt thiết lập từ trước thì GC sẽ được kích hoạt, lúc này GC sẽ tạm dừng chương trình và thực hiện công việc như sau:
- Duyệt trên GC Map để tìm và đánh dấu cách đối tượng theo 2 trạng thái:
- ALIVE: còn có đối tượng khác tham chiếu đến nó, hay là đang được sử dụng
- NOT ALIVE (garbage): không còn đối tượng nào tham chiếu đến nó.
- Các đối tượng nào được đánh dấu là garbage sẽ bị giải phóng ngay lập tức.
- Tiếp sau đó GC sẽ xây dựng lại GC Map và sắp xếp lại các vùng bộ nhớ rời rạc phân mảng thành các khối liên tục.
- Lúc này GC đã hoàn thành nhiệm vụ và sẽ đi vào trạng thái chờ (đến lần gọi tiếp theo) và resume lại chương trình để chương trình tiếp tục thực hiện tiếp công việc của mình.
Nhờ vào Garbage Collection mà việc lập trình trên Java trở nên dễ dàng hơn nhiều do các lập trình viên không cần phải quan tâm tới việc cấp phát và giải phóng bộ nhớ nữa. Nhưng cũng vì vậy mà nếu để GC chạy quá nhiều cũng sẽ làm giảm hiệu năng của chương trình, do mỗi lần hoạt động GC cần phải dừng chương trình của bạn trong 1 thời gian ngắn. Tuy thời gian này cực ngắn nhưng cũng làm tăng thời gian xử lý của chương trình 1 cách không cần thiết. Thậm chí nặng nề hơn khi mà heap size chương trình đạt ngưỡng mà GC không thể giải phóng bộ nhớ để cấp phát lại thì chương trình sẽ có thể bị khởi động lại hoặc kết thúc.
2.2. Nguyên nhân gây ra Memory Leak
Theo cơ chế làm việc của Garbage Collection thì các đối tượng khi không còn tham chiếu đến nữa sẽ được giải phóng. Mà Memory Leak là hiện tượng mà 1 hoặc nhiều vùng nhớ không sử dụng đến nữa không được giải phóng. Như vậy điều này đồng nghĩa với việc, khi Memory Leak xảy ra thì các vùng nhớ không được sử dụng đến nữa nhưng vẫn còn các tham chiếu từ các đối tượng khác, chính vì vậy mà GC mới không thể thu hồi các vùng nhớ này.
Ví dụ như hình bên dưới:
Với B là vùng nhớ không còn được sử dụng nữa, nhưng GC không thể giải phóng B do B vẫn đang được tham chiếu đến bởi A. Hơn nữa B còn đang giữ tham chiếu đến C và ngược lại C lại có tham chiếu đến A, nên nếu giải phóng B thì sẽ tiêu tốn tài nguyên để xây dựng lại GC Map, điều này cũng sẽ tiêu tốn càng nhiều tài nguyên nếu sơ đồ tham chiếu giữa các đối tượng càng phức tạp.
Vâng bài viết giới thiệu về Memory Leak của mình xin được phép kết thúc tại đây, hi vọng sau bài viết này các bạn đã hiểu hơn về Memory Leak và tác hại của nó. Ở phần sau mình sẽ gửi tới các bạn một số cách để phòng tránh Memory Leak trong ứng dụng của mình. Các bạn hãy đón xem nhé.
- Google+
- Wordpress