انواع راهکارهای پیشگیری از نشت حافظه (memory leak)


memory leak چیست و چگونه اتفاق می افتد؟

memory leak میتواند با وجود garbage collection حادث شود. در یکی از مقاله های پیشین به بررسی کارکرد garbage collector و مدیریت حافظه پرداختیم. اما می دانیم با وجود این فرآیند احتمال بروز نشت حافظه  وجود دارد.

وقتی objectهای زیادی بدون استفاده در heap وجود داشته باشند که هم چنان از طرف stack مورد ارجاع قرار میگیرند نشت حافظه اتفاق می افتد. در چنین حالتی garbage collector  حافظه درگیر شده را آزاد نمیکند.

نشت حافظه در اندروید

اما چه مواردی باعث بروز نشت حافظه می شوند؟

در اندروید راه های زیادی برای رسیدن به memory leak وجود دارد! هنگام استفاده از AsyncTask ،Listeners ،Handlers ،Singleton، تردها و…

در ادامه به بررسی چگونگی بروز نشت حافظه در هنگام استفاده از پترن Singleton و listeners که بواسطه سهل انگاری برنامه نویس رخ می دهد، خواهیم پرداخت. همینطور راه حل هایی برای مدیریت حافظه در این موقعیت ها ارائه می دهیم.

چگونه با استفاده از الگوی singleton میتوانیم باعث memory leak شویم؟

یک مثال کلی از این الگو را در نظر بگیرید که با گرفتن context قصد دارد مثلا از shared preference استفاده کند.

public class SingletonManager {
  

      private static SingletonManager singleton;
      private Context context;
  

      private SingletonManager(Context context) {
          this.context = context;
      }
  

      public synchronized static SingletonManager getInstance(Context context) {
          if (singleton == null) {
              singleton = new SingletonManager(context);
          }
          return singleton;
      }

بواسطه پاس دادن context به نمونه سراسری که singleton میسازد عمر اکتیویتی را هم با خودش افزایش می دهد.

public class LoginActivity extends Activity {
        @Override
      protected void onCreate(Bundle savedInstanceState) {
          //...
          SingletonManager.getInstance(this);
      }
  }

پس اکتیویتی از زمانی که نمونه سراسری را بسازیم تا زمان اجرای برنامه در شی singleton حمل خواهد شد!

 

نشت حافظه هنگام استفاده از Singleton

پیش از اینکه راهکاری برای این مشکل ارائه دهیم بهتر است مفهومی با عنوان PermGen را بررسی کنیم.

PermGen در واقع مخفف عبارت  permanent generationیا بخشی دائمی است و بخش اختصاصی از فضای heap می باشد که از بخش اصلی heap در جاوا جدا شده. متدها و متغیرهای استاتیک در قسمت permGen نگه داشته می شوند.

ساختار heap در اندروید

اما چگونه از بروز memory leak در این حالت پیشگیری کنیم؟

به جای پاس دادن context اکتیویتی، context اپلیکیشن را به getInstance  پاس دهید.

SingletonManager.getInstance(getApplicationContext());

و بطور کلی ارجاعاتی که طول عمرشان تا انتهای برنامه خواهد بود را از context اکتیویتی نگیرید.

یا میتوانید کلاس singleton خود را به نحوی تغییر دهید که هنگام ساخت نمونه در خود از  context اپلیکیشن استفاده کند تا دیگر لازم نباشد از بیرون حتما context اپلیکشین را به آن پاس دهید.

public class SingletonManager {
     // ....
      public synchronized static SingletonManager getInstance(Context context) {
          if (singleton == null) {
              singleton = new SingletonManager(context.getApplicationContext());
          }
          return singleton;
      }
  }

حالا از هر جای برنامه که بخواهید نمونه ای از singleton بسازید دیگر فرقی ندارد چه contextی به آن پاس داده شود.

چگونه استفاده از listenerها میتواند منجر به memory leak شود؟

رایج ترین روش برای ساخت callbackها در توسعه اندروید الگوی listener یا observer است و موقعیت های زیادی در برنامه شما وجود خواهد داشت که یک listener را در اکتیویتی یا فرگمنت مربوطه قرار دهید اما فراموش کنید آنرا غیر فعال (unregister) نمایید. وقتی شما اکتیویتی مورد نظرتان را بعنوان listener پاس میدهید باید کاملا آگاه باشید که objectهای دیگر چه اعمالی را روی اکتیوتی پیاده میکنند.

public class LoginActivity extends Activity implements LocationListener {
  

    @Override
    public void onLocationUpdated(Location location){
    // do something
    }
    
    @Override
    protected void onStart(){
      LocationManager.getInstance().register(this);
    }
    
    @Override
    protected void onStop(){
      LocationManager.getInstance().unregister(this);
    }
  

  }

در مثال بالا یک اکتیویتی را مسئول خبر دادن رویدادِ به روزرسانی مکان قرار داده ایم. اگر فراموش کنیم که اکتیوتی را از LocationManager غیرفعال کنیم در واقع باعث میشود اکتیویتی از بین برود و فریم ورک اندروید متد onDestroy() را صدا خواهد زد. اما garbage collector قادر نخواهد بود تا نمونه ساخته شده از اکتیوتی را حافظه حذف کند زیرا LocationManager هم چنان ارجاعی به این نمونه را در خود نگه داشته است.

و اما راه حل:

مطمئن شوید در متد onDestroy() یا onStop() تمام listenerهای خود را غیر فعال نمایید.

protected void onDestroy() {
      unregisterReceivers();
      LocationManager.getInstance().unregister(this);  
      AppState.getInstance().setStateChangeListener(null);
      LocalBroadcastManager.getInstance(getApplicationContext())
          .unregisterReceiver(messageReciver);
      super.onDestroy();
  }

در این مقاله به بررسی دو دلیل عمده در بروز memory leak پرداختیم. از آنجایی که همیشه راه بهتری وجود دارد در بین نظرات شما به دنبال یادگیری راه های بهتر برای پیشگیری از نشت حافظه در اندروید هستیم.

به این پست امتیاز دهید

روی ستاره های کلیک کنید و امتیاز بدید

میانگین امتیاز 4.2 / 5. تعداد: 6

از امتیاز شما متشکریم

2 دیدگاه در نوشته: “انواع راهکارهای پیشگیری از نشت حافظه (memory leak)

  1. مصی گفت:

    عالی بود دم شما گرم

    1. نسیم نژند گفت:

      زنده باشید ممنون از شما 🙂

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

Enter Captcha Here : *

Reload Image