Uygulamanızın belleğini yönetme

Bu sayfada, uygulamanızda bellek kullanımını proaktif olarak nasıl azaltabileceğiniz açıklanmaktadır. Android işletim sisteminin belleği nasıl yönettiğiyle ilgili bilgi için Bellek yönetimine genel bakış'a göz atın.

Rastgele erişimli bellek (RAM), tüm yazılım geliştirme ortamları için değerli bir kaynaktır ve fiziksel belleğin genellikle kısıtlı olduğu mobil işletim sistemlerinde daha da değerlidir. Hem Android Çalışma Zamanı (ART) hem de Dalvik sanal makinesi rutin çöp toplama işlemi gerçekleştirse de bu, uygulamanızın belleği ne zaman ve nerede ayırıp serbest bıraktığını göz ardı edebileceğiniz anlamına gelmez. Genellikle statik üye değişkenlerindeki nesne referanslarını tutmaktan kaynaklanan bellek sızıntıları yaşamaktan kaçınmanız ve Reference nesnelerini yaşam döngüsü geri çağırmalarıyla tanımlanan uygun zamanda yayınlamanız gerekir.

Kullanılabilir belleği ve bellek kullanımını izleme

Uygulamanızın bellek kullanımı sorunlarını düzeltmeden önce bulmanız gerekir. Android Studio'daki Bellek Profil Aracı, bellek sorunlarını aşağıdaki şekillerde bulmanıza ve teşhis etmenize yardımcı olur:

  • Uygulamanızın zaman içinde belleği nasıl tahsis ettiğini görün. Bellek Profil Aracı, uygulamanızın kullandığı bellek miktarını, ayrılan Java nesnelerinin sayısını ve atık toplama işleminin ne zaman gerçekleştiğini gösteren gerçek zamanlı bir grafik gösterir.
  • Uygulamanız çalışırken çöp toplama etkinliklerini başlatın ve Java yığınının anlık görüntüsünü alın.
  • Uygulamanızın bellek ayırmalarını kaydedin, ayrılan tüm nesneleri inceleyin, her ayırma için yığın izlemeyi (stack trace) görüntüleyin ve Android Studio düzenleyicisinde ilgili koda atlayın.

Etkinliklere yanıt olarak anıyı serbest bırakın

Android, Bellek yönetimine genel bakış bölümünde açıklandığı gibi, önemli görevler için bellekte yer açmak amacıyla gerektiğinde uygulamanızın belleğini geri alabilir veya uygulamanızı tamamen durdurabilir. Sistem belleğini dengelemek ve sistemin uygulama işleminizi durdurmasını önlemek için ComponentCallbacks2 arayüzünü Activity sınıflarınıza uygulayabilirsiniz. Sağlanan onTrimMemory() geri çağırma yöntemi, uygulamanız ön veya arka planda çalışırken bellekle ilgili etkinlikleri dinlemesini sağlar. Ardından uygulamanız, uygulamanın yaşam döngüsü veya sistemin belleği geri alması gerektiğini gösteren sistem etkinliklerine yanıt olarak nesneleri serbest bırakmasını sağlar.

Aşağıdaki örnekte gösterildiği gibi, bellekle ilgili farklı etkinliklere yanıt vermek için onTrimMemory() geri çağırmasını uygulayabilirsiniz:

Kotlin

import android.content.ComponentCallbacks2
// Other import statements.

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {

    // Other activity code.

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that is raised.
     */
    override fun onTrimMemory(level: Int) {

        // Determine which lifecycle or system event is raised.
        when (level) {

            ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
                /*
                   Release any UI objects that currently hold memory.

                   The user interface moves to the background.
                */
            }

            ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE,
            ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
            ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> {
                /*
                   Release any memory your app doesn't need to run.

                   The device is running low on memory while the app is running.
                   The event raised indicates the severity of the memory-related event.
                   If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system
                   begins stopping background processes.
                */
            }

            ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
            ComponentCallbacks2.TRIM_MEMORY_MODERATE,
            ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> {
                /*
                   Release as much memory as the process can.

                   The app is on the LRU list and the system is running low on memory.
                   The event raised indicates where the app sits within the LRU list.
                   If the event is TRIM_MEMORY_COMPLETE, the process is one of the
                   first to be terminated.
                */
            }

            else -> {
                /*
                  Release any non-critical data structures.

                  The app receives an unrecognized memory level value
                  from the system. Treat this as a generic low-memory message.
                */
            }
        }
    }
}

Java

import android.content.ComponentCallbacks2;
// Other import statements.

public class MainActivity extends AppCompatActivity
    implements ComponentCallbacks2 {

    // Other activity code.

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that is raised.
     */
    public void onTrimMemory(int level) {

        // Determine which lifecycle or system event is raised.
        switch (level) {

            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:

                /*
                   Release any UI objects that currently hold memory.

                   The user interface moves to the background.
                */

                break;

            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:

                /*
                   Release any memory your app doesn't need to run.

                   The device is running low on memory while the app is running.
                   The event raised indicates the severity of the memory-related event.
                   If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system
                   begins stopping background processes.
                */

                break;

            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:

                /*
                   Release as much memory as the process can.

                   The app is on the LRU list and the system is running low on memory.
                   The event raised indicates where the app sits within the LRU list.
                   If the event is TRIM_MEMORY_COMPLETE, the process is one of the
                   first to be terminated.
                */

                break;

            default:
                /*
                  Release any non-critical data structures.

                  The app receives an unrecognized memory level value
                  from the system. Treat this as a generic low-memory message.
                */
                break;
        }
    }
}

Ne kadar belleğe ihtiyacınız olduğunu kontrol etme

Android, birden fazla çalışan işleme izin vermek amacıyla her bir uygulamaya ayrılan yığın boyutuna kesin bir sınır uygular. Tam yığın boyutu sınırı, cihazın genel olarak ne kadar RAM'e sahip olduğuna bağlı olarak cihazlar arasında değişiklik gösterir. Uygulamanız yığın kapasitesine ulaşır ve daha fazla bellek ayırmaya çalışırsa sistem bir OutOfMemoryError bildirir.

Bellek yetersizliğini önlemek için mevcut cihazda ne kadar yığın alanı olduğunu belirlemek üzere sistemi sorgulayabilirsiniz. getMemoryInfo() yöntemini çağırarak bu sayı için sistemi sorgulayabilirsiniz. Bu işlem; kullanılabilir bellek, toplam bellek ve sistemin işlemleri durdurmaya başladığı bellek eşiği dahil olmak üzere cihazın geçerli bellek durumu hakkında bilgi sağlayan bir ActivityManager.MemoryInfo nesnesi döndürür. ActivityManager.MemoryInfo nesnesi, cihazın belleğinin azalıp azalmadığını söyleyen basit bir boole olan lowMemory özelliğini de açığa çıkarır.

Aşağıdaki örnek kod snippet'i, uygulamanızda getMemoryInfo() yönteminin nasıl kullanılacağını gösterir.

Kotlin

fun doSomethingMemoryIntensive() {

    // Before doing something that requires a lot of memory,
    // check whether the device is in a low memory state.
    if (!getAvailableMemory().lowMemory) {
        // Do memory intensive work.
    }
}

// Get a MemoryInfo object for the device's current memory status.
private fun getAvailableMemory(): ActivityManager.MemoryInfo {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    return ActivityManager.MemoryInfo().also { memoryInfo ->
        activityManager.getMemoryInfo(memoryInfo)
    }
}

Java

public void doSomethingMemoryIntensive() {

    // Before doing something that requires a lot of memory,
    // check whether the device is in a low memory state.
    ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();

    if (!memoryInfo.lowMemory) {
        // Do memory intensive work.
    }
}

// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
    ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
    activityManager.getMemoryInfo(memoryInfo);
    return memoryInfo;
}

Daha fazla bellek verimli kod yapısı kullanın

Bazı Android özellikleri, Java sınıfları ve kod yapıları diğerlerinden daha fazla bellek kullanır. Kodunuzda daha verimli alternatifler seçerek uygulamanızın kullandığı bellek miktarını en aza indirebilirsiniz.

Hizmetleri tutumlu bir şekilde kullanma

Gereksizken hizmetleri çalışır durumda bırakmamanızı önemle tavsiye ederiz. Hizmetleri gereksiz yere çalışır durumda bırakmak, bir Android uygulamasının yapabileceği en kötü bellek yönetimi hatalarından biridir. Uygulamanızın arka planda çalışması için bir hizmete ihtiyacı varsa bir iş çalıştırması gerekmediği sürece çalışır durumda bırakmayın. Görevini tamamladığında hizmetinizi durdurun. Aksi takdirde bellek sızıntısına neden olabilirsiniz.

Bir hizmeti başlattığınızda sistem, söz konusu hizmete ilişkin işlemin sürdürülmesini tercih eder. Bir hizmet tarafından kullanılan RAM diğer işlemler için kullanılamadığından bu davranış hizmet işlemlerini çok pahalı hâle getirir. Bu, sistemin LRU önbelleğinde tutabileceği önbelleğe alınan işlemlerin sayısını azaltır ve uygulama geçişinin daha az verimli olmasını sağlar. Hatta bellek dar olduğunda ve sistem, çalışmakta olan tüm hizmetleri barındırmak için yeterli işlemleri sürdüremediğinde sistemde aşırı harcamalara neden olabilir.

Genellikle, kullanılabilir belleğe uyguladıkları devam eden talepler nedeniyle kalıcı hizmetleri kullanmaktan kaçının. Bunun yerine, WorkManager gibi alternatif bir uygulama kullanmanızı öneririz. Arka plan işlemlerini planlamak için WorkManager uygulamasını kullanma hakkında daha fazla bilgi edinmek için Kalıcı çalışma bölümünü inceleyin.

Optimize edilmiş veri kapsayıcıları kullanma

Programlama dili tarafından sağlanan sınıflardan bazıları mobil cihazlarda kullanım için optimize edilmemiştir. Örneğin, genel HashMap uygulaması, her eşleme için ayrı bir giriş nesnesine ihtiyaç duyduğundan bellek açısından verimsiz olabilir.

Android çerçevesi, optimize edilmiş SparseArray, SparseBooleanArray ve LongSparseArray gibi çeşitli veri kapsayıcıları içerir. Örneğin, SparseArray sınıfları daha verimlidir çünkü sistemin anahtarı ve bazen değeri otomatik kutulama ihtiyacını ortadan kaldırır. Bu da giriş başına bir veya iki nesne daha oluşturur.

Gerekirse yalın bir veri yapısı için her zaman ham dizilere geçebilirsiniz.

Kod soyutlamalarında dikkatli olun

Geliştiriciler kod esnekliğini ve bakımını iyileştirebildikleri için genellikle soyutlamaları iyi bir programlama uygulaması olarak kullanırlar. Bununla birlikte soyutlamalar, genellikle yürütülmesi gereken daha fazla kod gerektirdiği ve kodu belleğe eşlemek için daha fazla zaman ve RAM gerektirdiği için önemli ölçüde daha maliyetlidir. Soyutlamalarınız size fayda sağlamayacaksa onlardan kaçının.

Serileştirilmiş veriler için Lite protobuf'ları kullanma

Protokol arabellekleri (protokol arabellekleri), Google tarafından yapılandırılmış verileri serileştirmek için tasarlanmış, dilden bağımsız, platformdan bağımsız ve genişletilebilir bir mekanizmadır. XML'e benzer ancak daha küçük, daha hızlı ve daha basittir. Verileriniz için protobuf kullanıyorsanız istemci tarafı kodunuzda her zaman Lite protobuf'ları kullanın. Normal protokol arabellekleri son derece ayrıntılı kodlar üretir. Bu da uygulamanızda daha fazla RAM kullanımı, APK boyutunun önemli ölçüde artması ve yürütmenin yavaşlaması gibi birçok soruna neden olabilir.

Daha fazla bilgi için protobuf readme'ye bakın.

Bellek karmaşasından kaçının

Atık toplama etkinlikleri uygulamanızın performansını etkilemez. Ancak kısa bir sürede gerçekleşen birçok çöp toplama etkinliği, pili hızla tüketebilir ve çöp toplayıcı ile uygulama iş parçacıkları arasındaki gerekli etkileşimler nedeniyle çerçeve oluşturma süresini marjinal şekilde uzatabilir. Sistem çöp toplama için ne kadar çok süre harcarsa pil o kadar hızlı biter.

Bellek karmaşası, genellikle çok sayıda atık toplama etkinliğinin gerçekleşmesine neden olabilir. Uygulamada, bellek karmaşası, belirli bir süre içinde oluşan ayrılmış geçici nesnelerin sayısını ifade eder.

Örneğin, bir for döngüsü içinde birden çok geçici nesne ayırabilirsiniz. Alternatif olarak, bir görünümün onDraw() işlevinin içinde yeni Paint veya Bitmap nesneleri de oluşturabilirsiniz. İki durumda da uygulama yüksek hacimde hızlı bir şekilde çok sayıda nesne oluşturur. Bu dosyalar, genç neslin mevcut tüm belleğini hızla tüketerek atık toplama etkinliğinin gerçekleşmesini zorunlu kılar.

Düzeltmeden önce bellek karmaşasının yüksek olduğu yerleri kodunuzda bulmak için Bellek Profil Aracı'nı kullanın.

Kodunuzdaki sorunlu alanları belirledikten sonra, performans açısından kritik alanlardaki ayırma sayısını azaltmaya çalışın. Öğeleri iç döngülerin dışına taşımayı veya fabrika tabanlı bir ayırma yapısına taşımayı düşünebilirsiniz.

Ayrıca, nesne havuzlarının kullanım alanına fayda sağlayıp sağlamadığını da değerlendirebilirsiniz. Bir nesne havuzuyla, bir nesne örneğini zemine bırakmak yerine, artık ihtiyaç duyulmadığında bir havuza serbest bırakırsınız. Bir dahaki sefere bu tür bir nesne örneği gerektiğinde bunu ayırmak yerine havuzdan alabilirsiniz.

Bir nesne havuzunun belirli bir durumda uygun olup olmadığını belirlemek için performansı baştan sona değerlendirin. Nesne havuzlarının performansı düşürebileceği durumlar vardır. Havuzlar tahsis etmekten kaçınsa da başka ek maliyetlere yol açar. Örneğin, havuzun sürdürülmesi genellikle göz ardı edilemeyecek kadar ek yüke sahip senkronizasyon yapmayı içerir. Ayrıca, yayınlama sırasında bellek sızıntılarını önlemek için havuz nesne örneğini temizlemek ve ardından edinme sırasında başlatma işlemi sıfır dışında bir yük oluşturabilir.

Havuzda gerekenden daha fazla nesne örneğini tutmak çöp toplama için yük de oluşturur. Nesne havuzları atık toplama çağrılarının sayısını azaltırken canlı (erişilebilir) bayt sayısıyla orantılı olduğundan her çağrı için gereken iş miktarını artırır.

Yoğun bellek kullanan kaynakları ve kitaplıkları kaldırın

Kodunuzdaki bazı kaynaklar ve kitaplıklar, siz fark etmeden bellek tüketebilir. Üçüncü taraf kitaplıklar veya yerleşik kaynaklar da dahil olmak üzere uygulamanızın genel boyutu, uygulamanızın tükettiği bellek miktarını etkileyebilir. Gereksiz, gereksiz veya şişmiş bileşenleri ya da kaynakları ve kitaplıkları kodunuzdan kaldırarak uygulamanızın bellek tüketimini iyileştirebilirsiniz.

Genel APK boyutunu küçültme

Uygulamanızın genel boyutunu azaltarak uygulamanızın bellek kullanımını önemli ölçüde azaltabilirsiniz. Bitmap boyutu, kaynaklar, animasyon kareleri ve üçüncü taraf kitaplıkların tümü uygulamanızın boyutuna katkıda bulunabilir. Android Studio ve Android SDK, kaynaklarınızın ve harici bağımlılıkların boyutunu azaltmaya yardımcı olacak birden fazla araç sağlar. Bu araçlar, R8 derlemesi gibi modern kod daraltma yöntemlerini destekler.

Uygulamanızın genel boyutunu küçültme hakkında daha fazla bilgi için Uygulamanızın boyutunu küçültme konusuna bakın.

Bağımlılık ekleme için Hilt veya Dagger 2'yi kullanın

Bağımlılık ekleme çerçeveleri yazdığınız kodu basitleştirebilir, ayrıca testler ve diğer yapılandırma değişiklikleri için yararlı olan uyarlanabilir bir ortam sağlayabilir.

Uygulamanızda bir bağımlılık yerleştirme çerçevesi kullanmayı planlıyorsanız Hilt veya Dagger'ı kullanabilirsiniz. Hilt, Dagger'ın üzerinde çalışan Android için bir bağımlılık yerleştirme kitaplığıdır. Dagger, uygulamanızın kodunu taramak için yansıma kullanmaz. Dagger'ın statik derleme zamanı uygulamasını, gereksiz çalışma zamanı maliyeti veya bellek kullanımı olmadan Android uygulamalarında kullanabilirsiniz.

Yansıma başlatma işlemlerini kullanan diğer bağımlılık yerleştirme çerçeveleri, kodunuzu tarayarak ek açıklamaları arar. Bu işlem çok daha fazla CPU döngüsü ve RAM gerektirebilir ve uygulama başlatıldığında gözle görülür bir gecikmeye neden olabilir.

Harici kitaplık kullanırken dikkatli olun

Harici kitaplık kodu, genellikle mobil ortamlar için yazılmaz ve mobil istemcide çalışırken verimsiz olabilir. Harici bir kitaplık kullandığınızda bu kitaplığı mobil cihazlar için optimize etmeniz gerekebilir. Bu çalışmayı önceden planlayın ve kitaplığı kullanmadan önce kod boyutu ve RAM ayak izi açısından analiz edin.

Mobil cihazlar için optimize edilmiş bazı kitaplıklar bile farklı uygulamalar nedeniyle sorunlara neden olabilir. Örneğin, bir kitaplık lite protobuf kullanırken diğeri mikro protobuf kullanıyor olabilir. Bu da uygulamanızda iki farklı protobuf uygulamasına neden olur. Bu durum, farklı günlük kaydı, analiz, görüntü yükleme çerçeveleri, önbelleğe alma ve beklemediğiniz diğer birçok uygulamada gerçekleşebilir.

ProGuard, doğru flag'lerle API'lerin ve kaynakların kaldırılmasına yardımcı olabilse de kitaplığın büyük iç bağımlılıklarını kaldıramaz. Bu kitaplıklarda olmasını istediğiniz özellikler daha düşük düzeyli bağımlılıklar gerektirebilir. Kitaplıklar yansıma kullandığında, bir kitaplıktan Activity alt sınıfı kullandığınızda bu özellikle sorun haline gelir. Bu da yaygın olarak görülen ve bunun çalışması için ProGuard'ın manuel olarak ayarlanmasını gerektirir.

Onlarca özellikten yalnızca bir veya iki tanesi için paylaşılan bir kitaplık kullanmaktan kaçının. Kullanmadığınız büyük miktarda kod ve ek yük çekmeyin. Kitaplık kullanıp kullanmayacağınıza karar verirken ihtiyaçlarınıza kesinlikle uyan bir uygulama bulmaya çalışın. Aksi takdirde, kendi uygulamanızı oluşturmaya karar verebilirsiniz.