- vừa được xem lúc

Memory Leak và nguyên nhân

0 0 109

Người đăng: Nguyen Duc Anh C

Theo Viblo Asia

OutOfMemoryError (OOM) là thứ mà mọi Android Developer đều gặp phải. OutOfMemoryError xuất hiện trong Android do leak memory. Vì vậy, để loại bỏ OutOfMemoryError, bạn cần khắc phục leak memory khỏi ứng dụng Android của mình.

Memory leak

Khi bạn chạy một số ứng dụng trên thiết bị Android, thì hệ thống Android sẽ cung cấp một số không gian bộ nhớ để ứng dụng của bạn hoạt độngvà tất cả việc tạo biến, tạo hàm, tạo hoạt động, v.v. chỉ diễn ra trong bộ nhớ đó.

Ví dụ: nếu Hệ thống Android phân bổ 100 MB cho ứng dụng của bạn, thì ứng dụng của bạn có thể sử dụng tối đa 100 MB tại một thời điểm cụ thể. Theo thời gian, nếu không gian bộ nhớ được cấp cho ứng dụng bị giảm đi hoặc còn lại rất ít dung lượng, thì Garbage Collector (GC) sẽ giải phóng bộ nhớ đang được giữ bởi các biến và hoạt động không được sử dụng. Bằng cách này, ứng dụng sẽ lại có được một số dung lượng.

Nhưng đôi khi, bạn giữ các tham chiếu của các đối tượng không được yêu cầu nữa, thì trong tình huống này, GC không thể giải phóng không gian chưa sử dụng và không còn dung lượng cho ứng dụng cho nó hoạt động hơn nữa.

GC hoạt động như thế nào?

GC là quá trình các chương trình Android thực hiện quản lý bộ nhớ tự động bằng cách sử dụng một số thuật toán GC, ví dụ như Mark and Sweep.

Bất cứ khi nào một ứng dụng Android khởi động, nó sẽ tạo ra một cây bộ nhớ coi đối tượng bắt đầu là gốc của cây. Root tạo ra một số đối tượng khác có thể có tham chiếu của nó trực tiếp hoặc gián tiếp và một số đối tượng khác được khởi tạo từ các đối tượng này dẫn đến một chuỗi tham chiếu và do đó tạo thành cây bộ nhớ.

Trình thu gom rác bắt đầu từ GC root và đi qua từng và mọi tham chiếu đối tượng được liên kết trực tiếp hoặc gián tiếp với gốc và đánh dấu nó là hiện đang được sử dụng.

Bây giờ, chỉ cần nghĩ đến tình huống trong đó một số đối tượng không sử dụng cần xóa khỏi bộ nhớ đang được GC đánh dấu là hoạt động chỉ vì ai đó có tham chiếu về nó. Như vậy, nó sẽ không đủ điều kiện để thu gom rác dẫn đến Memory leak

Một số nguyên nhân gây ra memory leak

1. Unregistered Listeners

Có nhiều trường hợp khi chúng ta register 1 listener vào Activity hoặc Fragment nhưng lại unregister nó. Các listener này giữ tham chiếu đến nó và ngăn nó được giải phóng bởi GC ngay cả khi nó không được sử dụng.

public class RegisterListenerActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override protected void onStart() { super.onStart(); registerLocationUpdates(); } @Override protected void onStop() { super.onStop(); unRegisterLocationUpdates(); } private void registerLocationUpdates(){ // register for location updates here } private void unRegisterLocationUpdates(){ // unregister here }
}

2. Unregistered Local Broadcast receivers

Đôi khi chúng ta cần đăng ký một LocalBroadcastReceivers trong một activity

Tuy nhiên, nếu chúng ta không hủy đăng ký, nó sẽ giữ tham chiếu đến Actviity. Ngay cả khi activity không còn cần thiết, tham chiếu sẽ ngăn nó được GC thu dọn

public class LocalBroadcastReceiverActivity extends Activity { private BroadcastReceiver localBroadcastReceiver; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } private void registerBroadCastReceiver() { localBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //Write your code here. } }; registerReceiver(localBroadcastReceiver, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")); } @Override protected void onStart() { super.onStart(); registerBroadCastReceiver(); } @Override protected void onStop() { super.onStop(); /* * Broadcast receiver holds the implicit reference of Activity.Therefore even if activity is destroy, * garbage collector will not be able to remove its instance. * */ if(localBroadcastReceiver != null) { unregisterReceiver(localBroadcastReceiver); } }
}

3. Inner Class Reference

Inner Class rất phổ biến được sử dụng bởi nhiều developer để thực hiện các tác vụ khác nhau, tuy nhiên, lớp Non-static Inner Class vô tình giữ tham chiếu ngầm của lớp cha nên có thể dẫn đến memory leak

Giải pháp:

  • Sử dụng static inner class
public class InnerAsyncTaskReferenceActivity extends AppCompatActivity { /* * Following mistakes should be avoided in this scenario. * Mistake 1. Try to avoid referencing a class inside an activity because normal inner class holds implicit reference of parent class. * If at we need to do it,make it private static class which does not hold any implicit reference. * Mistake 2. Never use a direct reference of a view, reference of Activity itself or context from activity inside an Asynctask. * Mistake 3. Specially in case of AsycTask Inner class : We should always cancel the asyncTask inside onDestroy of Activity because the asyncTask will still be running even if the activity * is destroyed preventing activity to be garbage collected. * * */ private TextView textView; private BackgroundOperation backgroundOperation; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.inner_async_task_activity); /* * Initiate & execute AsyncTask here! * */ backgroundOperation = new BackgroundOperation(textView); backgroundOperation.execute(); } private class BackgroundOperation extends AsyncTask<Void, Void, String> { //private View view; private WeakReference<TextView> viewWeakReference; BackgroundOperation(TextView textView){ //this.view = view; // Problem 2 // Instead use WeakReference this.viewWeakReference = new WeakReference<>(textView); } @Override protected String doInBackground(Void... voids) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return "The task is completed!"; } @Override protected void onPostExecute(String value) { super.onPostExecute(value); //textView.setText(s); TextView view = viewWeakReference.get(); if(view != null) { view.setText(value); } } } @Override protected void onDestroy() { super.onDestroy(); /* * Always cancel AsyncTask when activity is destroyed. */ if(backgroundOperation!= null) { backgroundOperation.cancel(true); } }
}

4. Các tình huống khác:

  • Static view, context, activity
  • Truyền tham chiếu Activity hoặc Context cho một lớp Singleton.
  • Sử dụng Bitmap mà không recycle. Điều này có thể dẫn đến OutOfMemoryError. Vui lòng tìm cách xử lý thích hợp tại đây.

TÀI LIỆU THAM KHẢO

https://medium.com/@amritlalsahu5/all-about-memory-leaks-in-android-7c0e5c8d679c

Bình luận

Bài viết tương tự

- vừa được xem lúc

Cách Swift quản lý bộ nhớ với cơ chế ARC - Automatic Reference Counting

Tổng quan về bộ nhớ Stack và Heap. Chắc hẳn bất cứ ai trong chúng ta đều đã từng nghe đến khái niệm về 2 loại bộ nhớ Stack và Heap khi tìm hiểu về cách các ngôn ngữ lập trình quản lý và phân phối bộ nhớ của máy tính.

0 0 155

- vừa được xem lúc

Truy tìm memory leak xung quanh RecyclerView sử dụng LeakCanary

Overview. Bài viết này chủ yếu dành cho các lập trình viên mới làm quen với Android, những người chưa thực sự tìm hiểu sâu về LeakCanary.

0 0 52

- vừa được xem lúc

Context và Memory Leak trong Android

1. Context. . Context là trạng thái của ứng dụng tại một thời điểm nhất định.

0 0 32

- vừa được xem lúc

Leak Memory Ruby on Rails và cài đặt jemalloc

Bối cảnh. Chào mọi người ! Mình làm một dự án cũng khá là lâu, dự án được cái cũng có số lượng người dùng tương đối, nên cũng may mắn được trải nghiệm vấn đề như thế này:.

0 0 31

- vừa được xem lúc

Leak memory do sử dụng map sai cách trong Golang?

Như các bạn đã biết thì map là một data type được dựng sẵn trong Golang và nó là một tập hợp các cặp key/value. Ủa, đơn giản như vậy mà lại còn được buildin nữa thì sao lại dẫn đến leak memory được, đ

0 0 19

- vừa được xem lúc

[Golang Common Mistakes] Sai lầm khi sử dụng slicing slice trong Golang

Chào mừng các bạn đã quay trở lại với series những common mistakes trong Golang. Trong bài này, chúng ta sẽ tìm hiểu về.

0 0 23