Sửa lỗi rò rỉ bộ nhớ trong Android - OutOfMemoryError
Bài đăng này đã không được cập nhật trong 3 năm
Việc rò rỉ bộ nhớ trong Android là khá dễ xảy ra. Developer có thể không nhận ra đã để bộ nhớ bị rò rỉ mỗi ngày. Cho đến khi nhảy ra một ngoại lệ như thế này...
java.lang.OutOfMemoryError: Failed to allocate a 4308492 byte allocation with 467872 free bytes and 456KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:988)
at android.content.res.Resources.loadDrawableForCookie(Resources.java:2580)
at android.content.res.Resources.loadDrawable(Resources.java:2487)
at android.content.res.Resources.getDrawable(Resources.java:814)
at android.content.res.Resources.getDrawable(Resources.java:767)
at com.nostra13.universalimageloader.core.DisplayImageOptions.getImageOnLoading(DisplayImageOptions.java:134)
Exception này có phải nói rằng bitmap quá lớn đối với hệ thống Android?
Thật không may, khi nhận được một OutOfMemoryError
, thì nó thường có nghĩa là, 9/10 đã xảy ra rò rỉ bộ nhớ. Ấn tượng đầu tiên khi đọc đoạn Stack trace trên, nghĩ là bitmap quá lớn, như vậy là nhận định sai lầm.
Rò rỉ bộ nhớ là gì?
A failure in a program to release discarded memory, causing impaired performance or failure.
Tức là: một lỗi trong chương trình khi giải phóng bộ nhớ không dùng tới, làm ảnh hưởng đến hiệu năng hệ thống hoặc xảy ra lỗi.
Rò rỉ bộ nhớ xảy ra trong Android?
Rò rỉ bộ nhớ trong Android thực sự khá dễ dàng để xảy ra. Vấn đề lớn nhất là các đối tượng Context
.
Mỗi ứng dụng có một đối tượng Context
toàn cục (getApplicationContext()
). Mọi Activity
là một lớp con của Context
, lưu giữ thông tin liên quan đến Activity
hiện tại. Rò rỉ bộ nhớ thường xuyên được liên kết với một Activity
bị rò rỉ. Chẳng hạn, một vài đối tượng TextView
được khai báo static
sẽ giữ tham chiếu tới Activity
, dẫn tới Activity
này sẽ không được giải phóng khi không còn sử dụng, bộ nhớ bị chiếm dụng không cần thiết.
Một dấu hiệu cảnh báo lớn cho thấy bộ nhớ được sử dụng ngày càng tăng trong ứng dụng, như mô tả trong Androids Memory Monitor:
Androids Memory Monitor của ứng dụng xảy ra rò rỉ bộ nhớ
Androids Memory Monitor sau khi sửa lỗi rò rỉ bộ nhớ
Trong đồ thị đầu tiên, ta có thế thấy ứng dụng sẽ không bao giờ có thể lấy lại một số bộ nhớ đã sử dụng. Nó được sử dụng lên đến khoảng 300MB trước thời điểm xảy ra OutOfMemoryError
. Biểu đồ thứ 2 cho thấy rằng ứng dụng có thể thu dọn rác, lấy lại một số bộ nhớ đã cung cấp và sử dụng bộ nhớ khá thích hợp.
Làm thế nào để tránh rò rỉ bộ nhớ?
- Tránh truyền thêm đối tượng
Context
vàoActivity
hoặcFragment
. - KHÔNG BAO GIỜ tạo hoặc lưu một đối tượng
Context
hoặcView
trong một biếnstatic
. Đây là dấu hiệu đầu tiên của rò rỉ bộ nhớ.
private static TextView textView; //DO NOT DO THIS
private static Context context; //DO NOT DO THIS
- Luôn luôn
unregister
cáclistener
trong phương thứconPauser()
hoặconDestroy()
. Nó bao gồm cáclistener
của Android, đến những thứ nhưLocation service
hay cácservice
quản lý hiển thị và cáclistener
được tuỳ chỉnh riêng. - Không lưu trữ các tham chiếu mạnh (
strong refenrence
) tới cácactivity
trong cácAsyncTask
hoặc cácbackground thread
. Bởi vì cácActivity
có thể bị đóng lại, nhưngAsyncTask
vẫn tiếp tục thực hiện và giữ tham chiếu tớiActivity
. - Sử dụng
Context
của ứng dụng (getApplicationContext()
) thay vìContext
từ mộtActivity
nếu có thể. - Cố gắng tránh sử dụng các
non-static inner class
. Lưu trữ một tham chiếu tới một cái gì đó nhưActivity
hayView
bên trong lớp này có thể dẫn đến rò rỉ bộ nhớ. Sử dụngWeakReference
nếu cần tham chiếu tới chúng.
Làm thế nào để sửa lỗi rò rỉ bộ nhớ?
Sửa lỗi rò rỉ bộ nhớ sẽ phải thực hiện một số hoạt động, rất nhiều lần thử và lỗi. Rò rỉ bộ nhớ có thể rất khó để theo dõi. May mắn là có một vài công cụ trợ giúp xác định một rò rỉ có thể.
- Mở Android Studio, bật sang tab Android Monitor.
- Chạy ứng dụng và lựa chọn nó từ danh sách các ứng dụng.
- Thao tác một số hành động trong ứng dụng.
- Theo dõi quá trình ứng dụng sử dụng bộ nhớ cho đến khi xảy ra
OutOfMemoryException
. - Click tab Memory trong Android Monitor.
- Xuất hiện biểu đồ bắt đầu được vẽ. Khi sẵn sàng thì click “Initiate GC” (biểu tượng xe tải rác màu cam).
- Click “Dump Java Heap” và chờ đợi một vài giây. Hành động này sẽ tạo một tập tin .hprof để phân tích sử dụng bộ nhớ.
- Cài đặt MAT để xem phân tích file .hprof
- Chạy lệnh sau để chuyển đổi từ file .hprof sang file MAT.
./hprof-conv path/file.hprof exitPath/heap-converted.hprof
- Mở file trong MAT và lựa chọn “Leak Suspects Report".
- Click vào thanh 3 màu xanh trên cùng “Create a histogram from an arbitrary set of objects”, sẽ thấy danh sách các đối tượng được chiếm bộ nhớ.
- Lọc các đối tượng bằng tên class.
- Nhìn hình trên thấy rằng có 9 đối tượng
VideoDetailActivity
, rõ ràng là không đúng khi chỉ nên có 1 đối tượng. Để tìm những thứ giữ tham chiếu tớiVideoDetailActivity
, click chuột phải vào item và chọn “Merge Paths to Shortest GC Root”, sau đó click vào “exclude all phantom/weak/soft etc. references”.
- Từ thông tin dưới đây, có thể thấy một
DisplayListener
đã đượcregister
nhưng không bao giờ đượcunregistered
.
Vì vậy, rò rỉ bộ nhớ được giải quyết bằng cách gọi unregistered
cho listener
đã được register
.
DisplayManager displayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
displayManager.unregisterDisplayListener(listener);
Không phải tất cả các rò rỉ bộ nhớ được tìm ra dễ dàng, một số sẽ khó khăn hơn. Có nhiều công cụ khác trợ giúp cho việc tìm rò rỉ bộ nhớ, có thể xem qua tại: http://developer.android.com/intl/vi/tools/performance/comparison.html
All rights reserved