Fixing Memory Leaks in Android – OutOfMemoryError

Fixing Memory Leaks in Android – OutOfMemoryError

Out of memory error là lỗi rất phổ biến khi chúng ta phát triển một ứng dụng có giao diện với nhiều hình ảnh, bitmap lớn hoặc sử dụng nhiều Animation. Trong trường hợp này, ta phải rất cẩn thận và hiệu quả khi xử lý các hình ảnh hoặc đối tượng phân bổ và deallocation. lỗi OOM đến khi việc phân bổ vượt mức giới hạn cho phép hoặc quá trình của bạn yêu cầu một số lượng bộ nhớ mà vượt quá giới hạn heap.

Trong Android, mọi ứng dụng chạy trong một tiến trình Linux. Mỗi trình Linux có một máy ảo (Dalvik Virtual Machine) chạy bên trong nó. Có một giới hạn về bộ nhớ là một quá trình có thể yêu cầu và nó là khác nhau cho các thiết bị khác nhau và cũng khác nhau cho điện thoại và máy tính bảng. Khi một số quá trình đòi hỏi một bộ nhớ cao hơn giới hạn của nó, nó gây ra Out Of Memory.

Possible Reasons:

Có nhiều lý do tại sao chúng ta bị Out Of Memory

  1. Bạn đang bị rò rỉ một số bộ nhớ (leak memory) tức là bạn đã không làm cho các đối tượng trước đó bạn được giao đủ điều kiện cho Garbage collector (GC). Điều này được gọi là Leak Memory

  2. Bạn đang làm việc với nhiều bitmap lớn và chất lượng. Bạn phải cẩn thận với bitmap lớn bằng cách load kích thước insample size và tùy chỉnh Config của Bitmap (ARG_565, ALPHA_4,...) tùy mục đích hiển thị của bitmap.

Lý do lớn nhất cho tràn bộ nhớ là rò rỉ bộ nhớ (Leak Memory)

How do I avoid memory leaks?

  • NEVER EVER EVER make/store a Context or View in a static variable. This is the first sign of a memory leak.
private static TextView textView; //DO NOT DO THIS
private static Context context; //DO NOT DO THIS

private static TextView textView; //DO NOT DO THIS
private static Context context; //DO NOT DO THIS
  • Luôn unregister các listener trong onPause () / onDestroy (). Điều này bao gồm Listener của Android, đến những thứ như Dịch vụ định vị, dịch vụ quản lý hiển thị và listener tùy chỉnh của riêng bạn.
  • Cần cancle thread, Handler,... chạy trong Activity khi rời khỏi.
  • Bitmap lớn bằng cách load kích thước insample size và tùy chỉnh Config của Bitmap (ARG_565, ALPHA_4,...) tùy mục đích hiển thị của bitmap.
  • Sử dụng Context ứng dụng (getApplicationContext ()) thay vì Context từ Activity nếu có thể.
  • Cố gắng không sử dụng inner class non-static nếu bạn có thể tránh nó. Lưu trữ reference cho cái gì đó như Activity hoặc View bên trong này có thể dẫn đến rò rỉ bộ nhớ. Sử dụng WeakReference nếu bạn cần lưu trữ tham chiếu đến chúng.

How do I fix it?

Trong Android có nhiều công cụ có thể sử dụng để phát hiện memory leak và một trong số đó là MAT (Memory Analyzer Tool).

  1. Mở Android Studio, mở tab Android Monitor.
  2. Chạy ứng dụng, và chọn package name là ứng dụng bạn cần check để có thể xem log và track memory
  3. Vọt vạch ứng dụng, đi tất cả các màn hình cửa ứng dụng
  4. Nếu có thể hãy làm cho đến khi xuất hiện lỗi OutOfMemoryException.
  5. Chuyển sang tab Monitor.
  6. Click “Dump Java Heap” và đợi một thời gian. Nó sẽ generate file .hprof, dựa vào file này có thể phân tích memory.
  7. Android Studio chúng ta vẫn có thể phân tích được memory nhưng không đầy đủ như Eclipse Memory Analyzer.
  8. Sử dụng lên sau để convert file .hprof từ Android thành file mà MAT có thể hiểu. (tool hprof-conv tool được đặt ở folder platform tools của AndroidSDK) ./hprof-conv path/file.hprof exitPath/heap-converted.hprof
  9. Ở lần khởi động đầu tiên MAT. Chọn “Leak Suspects Report”
  10. click “Create a histogram from an arbitrary set of objects”. Bạn sẽ thấy list các class sử dụng memory.
  11. Bạn có thể filter bằng cách gõ package name của ứng dụng để loại bớt danh sách các class không cần check.
  12. Nhìn vào hình bên trên chung ta có thể thấy 9 instances của VideoDetailActivity, đáng lẻ phải có 1 instance . Để tìm hiểu những gì đang reference tới VideoDetailActivity , right click vào item và chọn “Merge Paths to Shortest GC Root”, và sau đó lick “exclude all phantom/weak/soft etc. references”.
  13. Từ những thông tin dưới đây ta thấy đối tượng DisplayListener đã registered nhưng không thấy unregistered. Chúng ta fix bằng cách unregister DisplayListener trong method onDestroy của VideoDetailActivity.
DisplayManager displayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
displayManager.unregisterDisplayListener(listener);

Không phải tất cả Leak Memory dễ dàng tìm thấy như thế này, một số thì khó tìm thấy hơn chúng ta phải đọc kĩ lại từng dòng code, nhưng hy vọng hướng dẫn này giúp bạn tìm ra nơi mà vấn đề và tránh những nguyên nhân gây leak memory.

REF: https://riggaroo.co.za/fixing-memory-leaks-in-android-outofmemoryerror/