Memory leak là gì cách tránh memory leak c++

Một trong những thứ mà dev tìm hiểu đầu tiên khi bắt đầu lập trình Android nhưng cũng hay bỏ qua nhất đó chính là Context.

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

4,

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

5,

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

6,

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

7,

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

8,

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

9 là những thuật ngữ thường được liên kết với Context. Hiển thị Toast, mở màn hình mới, tạo view mới hoặc lưu dữ liệu trong preferences là tất cả các hành động yêu cầu sử dụng Context làm đối số.

Đôi khi giải pháp rất đơn giản (như sử dụng

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

0 nếu chúng ta đang ở trong một

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

1 ), nhưng những lúc khác, chúng ta không chắc mình đang làm gì và chúng ta vẫn tự hỏi về điều đó…

Mục Lục

What is the Context?

An interface to the application environment.

Memory leak là gì cách tránh memory leak c++
Đậu xanh… OK… nhưng điều đó có nghĩa là gì…

Định nghĩa đó đang muốn nói với chúng ta rằng Context là một lớp có các phương thức để truy cập tài nguyên ứng dụng và dịch vụ hệ thống.

Để có được Context, chúng ta thường sử dụng

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

1 (this) vì vậy, chúng ta có thể suy luận rằng

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

1 chính là một

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

4.

Và thực tế thì Activity, Service hoặc Application là những triển khai cụ thể của lớp trừu tượng Context. Bên trong một ứng dụng có thể có một số Activities, một số Services và do đó, nhiều hơn một Context, nhưng chỉ có một Ứng dụng và do đó thì chỉ có một

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

  1. Memory leak là gì cách tránh memory leak c++

Context dùng để làm gì?

Các tình huống phổ biến nhất khi chúng ta cần sử dụng Context là khi sử dụng các Views (

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

4,

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

5,

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

7), khởi chạy Activities (

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

  1. hoặc truy cập các dịch vụ hệ thống (

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

8,

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

1). Nếu bạn đang tìm kiếm một cách phân loại chính thức hơn, thì có 4 loại sau:

  • Thu thập tài nguyên ứng dụng: res, assets, internal storage
  • Giao tiếp giữa các Activities : Intents
  • Truy cập các dịch vụ hệ thống: SystemServices
  • Get thông tin ứng dụng: ApplicationInfo

Có ba phương thức trả về một Context, chúng nằm trong các views, các activities và lớp

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

2, và mọi phương thức trong số chúng đều có chức năng riêng của nó.

Trong một view

Lớp View có phương thức

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

3 để lấy Context của Activity chứa nó. Là một Activity Context chứ không phải là một Application Context, nó có thể có thông tin về các chủ đề cụ thể làm thay đổi tính thẩm mỹ của activity cụ thể nào đó. Do đó, Activity Context là thứ cần thực hiện khi quản lý chế độ views, inflating layouts , khởi chạy activities, hiển thị dialog hoặc sử dụng các lớp ngắn hạn.

Hiểu thế nào cho đúng “lớp ngắn hạn” : classLifespan <= activityLifespan

Trong một activity

Lớp Activity là một kết thừa Context. Đây là Activity Context đã đề cập trước đó, chúng ta có thể truy cập nó bằng cách sử dụng từ khóa

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

0.

Phương thức bạn có thể sử dụng để lấy một Context khác là

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

5, như tên của nó, trả về context của ứng dụng, không phải của activity. Đây là Context của quá trình mà các activities chạy và nó được sử dụng trong các lớp vượt quá tuổi thọ của Activities, chẳng hạn như các tác vụ nền ( background tasks) hoặc quyền truy cập dữ liệu (data access).

Trong ContextWrapper

Đây là một lớp trung gian trong cây kế thừa và cung cấp phương thức

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

6. Khuyến khích là KHÔNG nên sử dụng nó cho dù là mục đích gì đi nữa.

Túm cái váy lại thì

Memory leak là gì cách tránh memory leak c++
Trong bảng này, chúng ta có sự kết hợp giữa các hành động với các loại Context.

Học cách quản lý đúng Context là một kỹ năng sẽ cứu chúng ta khỏi những vấn đề không mong muốn và điều đó có liên quan chặt chẽ với việc memory leak. Đó là lý do tại sao 2 thứ tưởng chừng không liên quan đến nhau lại ở trong 1 bài viết.

Memory Leaks

Memory Leaks là gì?

Memory Leaks xảy ra khi bộ thu gom rác phân bổ bộ nhớ cho một đối tượng nhưng không bao giờ thu hồi nó. Bộ thu gom rác nghĩ rằng đối tượng vẫn cần thiết vì nó được tham chiếu bởi các đối tượng khác, nhưng những tham chiếu đó lẽ ra đã được xóa.

Memory leak là gì cách tránh memory leak c++

Memory leak là gì cách tránh memory leak c++

Nếu điều này tiếp tục diễn ra, hệ thống có thể hết bộ nhớ heap và ứng dụng bị treo

Làm thế nào để tránh memory leaks trong Android?

Nguyên nhân phổ biến của memory leaks là các biến

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

7 , mẫu

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

8, tác vụ nền và các

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

9. Hãy xem một số ví dụ về cách phát hiện và khắc phục chúng

Static variable

  • Tránh giữ các static reference đến

    Object innerClass;

    0,

    Object innerClass;

    1,

    Object innerClass;

    2, và

    Object innerClass;

    3. Các static object tồn tại miễn là ứng dụng đang chạy và do đó mọi tham chiếu tĩnh đến view (hoặc context ) sẽ không bị xóa đúng lúc.

Trong ví dụ này, biến tĩnh

Object innerClass;

4 có tham chiếu đến

Object innerClass;

3 của

Object innerClass;

6:

static View vista;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// Static variable with reference to the activity context
  vista = new View(this);}

Nếu ứng dụng tiếp tục chạy sau khi Acitivity bị destroy, bộ nhớ nó đang sử dụng sẽ không được giải phóng vì biến

Object innerClass;

4 có tham chiếu đến context của activity.

Một giải pháp là bỏ tham chiếu đến biến bên trong phương thức

Object innerClass;

8.

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

Vấn đề tương tự có thể xảy ra nếu, khi sử dụng

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

8, nó đang lưu Activity Context. Thông thường, giải pháp tốt nhất, trong trường hợp đó, là sử dụng application context vì chúng ta được cho là ưa dùng kiến trúc như MVP, MVVM, do đó không cần truy cập trực tiếp vào các views và không cần activity context.

  • Tránh sử dụng context của Activity nếu có thể. Thay vào đó, hãy thử sử dụng context của ứng dụng (ví dụ: để tạo Room database instances, v.v.).
  • Tránh giữ các tham chiếu tĩnh đến các đối tượng sử dụng context để tự khởi tạo. Chúng ta không nên làm như thế này:

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

Inner class

new AsyncTask() {
  @Override
  protected Void doInBackground(Void... voids) {
    try {
      // Inner class with the context of the activity
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return null;
  }
}.execute();

0 là những lớp được tạo bên trong một lớp hoặc phương thức khác. Nếu chúng ta tạo inner class trong một Activity, thì inner class đó sẽ giữ một tham chiếu đến context của activity đó

staticObject innerClass;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);classInnerClass{// Automatic reference to Activity}
  innerClass =newInnerClass();}

Biến innerClass là tĩnh. Nếu nó là động, sự cố sẽ biến mất (say good bye):

Object innerClass;

Background task

Một tác vụ không đồng bộ có thể truy cập một activity và vẫn tiếp tục chạy nền sau khi activity đã chết. Trong đoạn code này, một AsyncTask được tạo bên trong một activity:

new AsyncTask() {
  @Override
  protected Void doInBackground(Void... voids) {
    try {
      // Inner class with the context of the activity
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return null;
  }
}.execute();

Một lần nữa, vấn đề là chúng ta đang tạo một

new AsyncTask() {
  @Override
  protected Void doInBackground(Void... voids) {
    try {
      // Inner class with the context of the activity
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return null;
  }
}.execute();

1. Nếu chúng ta đặt chung code bên trong một lớp kế thừa

new AsyncTask() {
  @Override
  protected Void doInBackground(Void... voids) {
    try {
      // Inner class with the context of the activity
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return null;
  }
}.execute();

2, vấn đề sẽ không còn nữa. Để chạy MyAsyncTask

new MyAsyncTask.execute();

Một Thread có thể dẫn đến cùng một sai lầm tương tự như trên , chúng ta có thể

new AsyncTask() {
  @Override
  protected Void doInBackground(Void... voids) {
    try {
      // Inner class with the context of the activity
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return null;
  }
}.execute();

3 luồng khi activity bị hủy để an toàn hơn:

Thread thread;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
  thread = new Thread(){@Overridepublic void run(){if(!isInterrupted()){// Reference to the context of the activity}}};
  thread.start();}@Overrideprotected void onDestroy(){super.onDestroy();
  thread.interrupt();}

Nhiều bạn có thể nghĩ là giờ người ta dùng coroutines , rx hết rời ai còn đi dùng mấy cái bị deprecated này làm gì. Nhưng thực tế rất nhiều dự án từ lâu đời vẫn dùng chúng để thực hiện các tác vụ đồng bộ và việc bảo trì luôn cần sự hiểu biết đúng đắn về những thứ base nhất như vậy.

Xác định lifecycle owner

  • Tránh binding leak bằng cách đặt

    new AsyncTask() { @Override protected Void doInBackground(Void... voids) {

    try {  
      // Inner class with the context of the activity  
      Thread.sleep(5000);  
    } catch (InterruptedException e) {  
      e.printStackTrace();  
    }  
    return null;  
    
    } }.execute();

    4 trong ở

    new AsyncTask() { @Override protected Void doInBackground(Void... voids) {

    try {  
      // Inner class with the context of the activity  
      Thread.sleep(5000);  
    } catch (InterruptedException e) {  
      e.printStackTrace();  
    }  
    return null;  
    
    } }.execute();

    5. Tài liệu chính thức của đã đề cập đến một cách tốt hơn để xử lý view binding.
  • Truyền

    new AsyncTask() { @Override protected Void doInBackground(Void... voids) {

    try {  
      // Inner class with the context of the activity  
      Thread.sleep(5000);  
    } catch (InterruptedException e) {  
      e.printStackTrace();  
    }  
    return null;  
    
    } }.execute();

    6 thay vì

    companionobject{val database =
        AppDB.getDB(MyApp.getInstance().applicationContext)}  
    

    0 trong khi observing LiveData object trong Fragment. Điều này là do

    new AsyncTask() { @Override protected Void doInBackground(Void... voids) {

    try {  
      // Inner class with the context of the activity  
      Thread.sleep(5000);  
    } catch (InterruptedException e) {  
      e.printStackTrace();  
    }  
    return null;  
    
    } }.execute();

    6 được gắn với fragment miễn là nó có UI (

    new AsyncTask() { @Override protected Void doInBackground(Void... voids) {

    try {  
      // Inner class with the context of the activity  
      Thread.sleep(5000);  
    } catch (InterruptedException e) {  
      e.printStackTrace();  
    }  
    return null;  
    
    } }.execute();

    9,

    new AsyncTask() { @Override protected Void doInBackground(Void... voids) {

    try {  
      // Inner class with the context of the activity  
      Thread.sleep(5000);  
    } catch (InterruptedException e) {  
      e.printStackTrace();  
    }  
    return null;  
    
    } }.execute();

    5). Mặt khác,

    companionobject{val database =
        AppDB.getDB(MyApp.getInstance().applicationContext)}  
    

    0 sẽ được gắn với vòng đời tổng thể của fragment (

    new MyAsyncTask.execute();

    2,

    Object innerClass;

    8).
    Memory leak là gì cách tránh memory leak c++
    )

viewModel.getImagesLiveData().observe(this, Observer{// update views})

Ở trên sẽ gây ra memory leak vì chúng ta đã truyền

companionobject{val database =  
        AppDB.getDB(MyApp.getInstance().applicationContext)}

0 với tư cách là lifecycle owner. Nếu view bị hủy (

new AsyncTask() {
  @Override
  protected Void doInBackground(Void... voids) {
    try {
      // Inner class with the context of the activity
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return null;
  }
}.execute();

5 được gọi) nhưng fragment thì không, (

Object innerClass;

8 không được gọi), thì mọi thay đổi đối với đối tượng LiveData vẫn được quan sát và có thể gây ra sự cố bất ngờ

Memory leak là gì cách tránh memory leak c++

  • Truyền

    new MyAsyncTask.execute();

    7 thay vì lifecycle của object trong constructor của

    new MyAsyncTask.execute();

    8 , bởi vì chúng ta muốn phạm vi của adapter là là vòng đời view của fragment chứ không phải là fragment

viewPager.adapter =MyFragmnentStatePagerAdapter(
        lifecycle = viewLifecycleOwner.lifecycle,
        fragmentManager = childFragmentManager,...)

  • Tránh rò rỉ liên quan đến ViewPager bằng cách thực hiện các thao tác sau trong onDestroyView (): set viewPager.adapter = null, xóa listeners được đặt trên các tab (nếu có) và detach TabLayoutMediator

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

0

  • Set adpater RecyclerView = null trong onDestroyView () để ngăn adapter giữ một tham chiếu đến đối tượng RecyclerView.

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

1

Khi sử register listener

Đảm bảo unregister tất cả các

new MyAsyncTask.execute();

9,

Thread thread;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
  thread = new Thread(){@Overridepublic void run(){if(!isInterrupted()){// Reference to the context of the activity}}};
  thread.start();}@Overrideprotected void onDestroy(){super.onDestroy();
  thread.interrupt();}

0, etc. theo vòng đời thích hợp để tránh memory leak. ví dụ. Nếu receiver được đăng ký trong

Thread thread;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
  thread = new Thread(){@Overridepublic void run(){if(!isInterrupted()){// Reference to the context of the activity}}};
  thread.start();}@Overrideprotected void onDestroy(){super.onDestroy();
  thread.interrupt();}

1, thì nó phải được hủy đăng ký trong

Thread thread;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
  thread = new Thread(){@Overridepublic void run(){if(!isInterrupted()){// Reference to the context of the activity}}};
  thread.start();}@Overrideprotected void onDestroy(){super.onDestroy();
  thread.interrupt();}

  1. Đây là ví dụ với Bound service, quá quen thuộc rồi phải không:

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

2

Vậy làm thế nào để xác định memory leaks khi code ?

  • Memory Profiler: Công cụ này là một phần của Android Studio và nó là cách nhanh nhất để tạo các

    Thread thread;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); thread = new Thread(){@Overridepublic void run(){if(!isInterrupted()){// Reference to the context of the activity}}}; thread.start();}@Overrideprotected void onDestroy(){super.onDestroy(); thread.interrupt();}

    3 và file hiển thị thông tin về mức tiêu thụ bộ nhớ.
    Memory leak là gì cách tránh memory leak c++
  • Leak Canary: Quá nổi tiếng rồi. Cài đặt thư viện này bên trong ứng dụng của chúng ta là có thể thấy dấu vết của tất cả các reference dẫn đến rò rỉ bộ nhớ trong thiết bị.

Thêm thằng này vào file build.gradle :

@Overrideprotected void onDestroy(){super.onDestroy();
    vista =null;}

3

Bây giờ hãy chạy ứng dụng. Mở qua mở lại các fragment và activitity. Cố gắng tạo thật nhiều object. Bạn sẽ thấy thông báo khi có bất kỳ rò rỉ nào đáng ngờ như bên dưới: