Bellek geri kazanma: çıkarma ve takas

Önceki bölümlerde, çekirdeğin sistem genelinde belleği nasıl yönettiğini görmüştük. Bu bölümde, Android'in tek tek uygulamaların bellek kullanımını bölümlendirmek ve kontrol etmek için kullandığı mekanizma olan Bellek Kontrol Grupları (memcg) hakkında daha ayrıntılı bilgi veriyoruz.

Sistemin geri kazanma için uygulamaları hedefleyebileceği veya uygulamalara bellek sınırları koyabileceği düzey memcg olduğundan, uygulama bellek ayak izlerinin proaktif olarak yönetilmesini sağlamak için memcg'yi anlamak çok önemlidir.

Bellek kontrol grupları (memcg)

Kontrol grupları (cgroup'lar), işlemleri hiyerarşik gruplar halinde düzenlemeye ve sistem kaynaklarını (CPU, bellek ve G/Ç gibi) bunlar arasında dağıtmaya olanak tanıyan bir Linux çekirdek özelliğidir. memcg, özellikle bellek için kullanılan cgroup denetleyicisidir.

Temel memcg kavramları

  • Memcg Ücreti: Bir memcg'deki bir işlem, anonim veya dosya destekli bir bellek sayfası ayırdığında bu sayfa memcg'ye "ücretlendirilir". Bir memcg'nin toplam ücreti, içindeki tüm işlemler tarafından kullanılan tüm sayfaların toplamıdır.
  • Hiyerarşik Muhasebe: Bellek kullanımı ağaçta yukarı doğru hesaplanır. Bir alt kontrol grubundaki ücret, üst kontrol grubunun kullanımına da yansıtılır.
  • Bellek Sınırları: Her memcg'nin, aşılması durumunda geri kazanmayı veya hatta OOM Killer'ı tetikleyen sınırları (ör. memory.max veya memory.high) olabilir. Bu sınırlar, genel sistem belleğinden bağımsızdır.
  • Memcg başına geri kazanma: Bir memcg sınırı aştığında veya sistemin belleğe ihtiyacı olduğunda çekirdek, geri kazanma için belirli bir memcg'yi hedefleyebilir. Bu, dosya sayfalarının çıkarılması veya anonim sayfalarının ZRAM'e takas edilmesi anlamına gelir.

Android memcg hiyerarşisi

Android, uygulama işlemlerini yönetmek için belirli bir hiyerarşi kullanır. Bu yapı, sistemin farklı uygulama türlerine (ör. ön plan ve arka plan) farklı politikalar uygulamasını sağlar.

Android memcg hiyerarşisi

  • /sys/fs/cgroup/apps/: Tüm Android uygulamalarının kökü.
  • uid_<UID>/: Her uygulamanın kullanıcı kimliği için bir dizin. Aynı uygulama paketine ait tüm işlemler bu grubu paylaşır.
  • pid_<PID>/: Her bir işlem ve ondan ayrılan tüm alt işlemler için bir dizin. Bu sayede, birden fazla süreci olan uygulamalar için ayrıntılı kontrol ve muhasebe yapılabilir.

Uygulamalı alıştırma: memcg'yi keşfetme

Bu alıştırmada, MemoryLab için memcg dizinini bulacak ve bellek yükünü gerçek zamanlı olarak gözlemleyeceksiniz.

1. MemoryLab'i başlatma

Cihazınızda MemoryLab'in çalıştığından emin olun.

2. MemoryLab'in memcg'sini bulma

Öncelikle, çalışan MemoryLab uygulamasının PID'sini alın:

adb shell pidof com.android.memorylab
# Example output: 11672

Şimdi cgroup dizinini bulun. Bu bilgiyi /proc/<PID>/cgroup bölümünde bulabilirsiniz:

adb shell cat /proc/11672/cgroup
# Example output: 0::/apps/uid_10274/pid_11672

cgroup v2'de, 0:: karakterinden sonraki yol, /sys/fs/cgroup karakterine göre memcg hiyerarşisini gösterir. Bu nedenle tam yol /sys/fs/cgroup/apps/uid_10274/pid_11672/ olur.

3. Read memcg stats

Bu dizine gidip anahtar dosyalara bakın:

# Current memory usage (in bytes)
adb shell cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.current
# Example output:
# 1591324672

memory.current, şu anda bu cgroup'a uygulanan toplam bellek miktarını (bayt cinsinden) gösterir.

# Detailed statistics
adb shell cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.stat | head -n 10
# Example output:
# anon 1585098752
# file 741376
# kernel 5484544
# kernel_stack 393216
# pagetables 4583424
# sec_pagetables 0
# percpu 216
# sock 0
# vmalloc 4096
# shmem 20480

memory.stat bir döküm sağlar: * anon: Anonim bellek miktarı (yığınlar, yığın izleme). * file: Dosya destekli bellek miktarı (sayfa önbelleği). * swap: ZRAM'e takas edilen bellek miktarı.

4. Değişiklikleri gözlemleme

  1. MemoryLab'i açın.
  2. memory.current değerini not edin.
  3. Java Belleği Ayır (10 MB)'a birkaç kez dokunun.
  4. memory.current bölümünü tekrar okuyun. Her dokunmada yaklaşık 10 MB arttığını görürsünüz.
  5. Bit Eşlemleri Ayır'a dokunun. anon artışını görmek için memory.stat bölümüne bakın.

memory.reclaim ile proaktif geri alma

memcg'nin (v2) güçlü bir özelliği memory.reclaim dosyasıdır. Bu dosyaya bir değer yazıldığında çekirdek, bu memcg'den ve altındaki tüm memcg'lerden hemen o miktarda belleği geri almaya çalışır.

Android'in memory.reclaim kullanım şekli

Android'in CachedAppOptimizer özelliği, uygulamalar dondurulduktan sonra bellekten en iyi şekilde yararlanmak için bu özelliği kullanır. Android Uygulama Dondurucu, önbelleğe alınan uygulamaların çalışmadıkları sırada mümkün olduğunca az RAM tüketmesini sağlar. Bir uygulama arka plana taşınıp dondurulduğunda sistem, uygulamanın mevcut bellek kullanımını memory.reclaim dosyasına yazar. Bu işlem, çekirdeği olası tüm dosya sayfalarını çıkarmaya ve tüm anonim sayfaları ZRAM'e taşımaya zorlayarak uygulamanın yerleşik ayak izini en aza indirir.

memory.current dosyasını okuyup memory.reclaim dosyasına geri yazarak aynı "maksimum geri kazanım" değerine manuel olarak da ulaşabilirsiniz:

# Force reclaim of everything
adb shell "cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.current > /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.reclaim"

Alıştırma: Geri alma ve izlemeye zorlama

Artık çekirdeği, MemoryLab'den belleği geri almaya ve etkinliği Perfetto izinde yakalamaya zorlayacağız.

  1. Hazırlık: MemoryLab'in bazı tahsisleri (Java ve Bitmaps) olduğundan emin olun.
  2. İzlemeyi Başlat: Planlama, geri alma ve sayfa önbelleği etkinliklerini yakalamak için bu satır içi izleme yapılandırmasını kullanın.

    # Use a config that captures scheduling, reclaim and page cache events
    adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/reclaim.perfetto-trace <<EOF
    buffers: {
        size_kb: 131072
        fill_policy: RING_BUFFER
    }
    data_sources: {
        config {
            name: "linux.ftrace"
            ftrace_config {
                ftrace_events: "sched/sched_switch"
                ftrace_events: "sched/sched_wakeup"
                ftrace_events: "kmem/rss_stat"
                ftrace_events: "mm_filemap_add_to_page_cache"
                ftrace_events: "mm_filemap_delete_from_page_cache"
                ftrace_events: "vmscan/mm_vmscan_direct_reclaim_begin"
                ftrace_events: "vmscan/mm_vmscan_direct_reclaim_end"
                ftrace_events: "vmscan/mm_vmscan_memcg_reclaim_begin"
                ftrace_events: "vmscan/mm_vmscan_memcg_reclaim_end"
                symbolize_ksyms: true
            }
        }
    }
    data_sources: {
        config {
            name: "linux.process_stats"
            process_stats_config {
                scan_all_processes_on_start: true
            }
        }
    }
    EOF
    
  3. Tetikleyici Basınç ve Geri Alma:

    • MemoryLab'de Allocate Native Memory (1GB) [Yerel Bellek Ayır (1 GB)] seçeneğine dokunun. Bu durum, sistem genelinde baskı oluşturur.
    • Ayrı bir terminalde 200 MB'ı geri alın:

      adb shell "echo 200M > /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.reclaim"
      
  4. Hata Geri Yükleme: MemoryLab'e geri dönün. Thrash Pagecache (Refault test) seçeneğine dokunun.

  5. İzlemeyi Durdur: İzleme terminalinde Ctrl+C tuşlarına basın.

Perfetto'da geri kazanma analizini yapma

İzlemeyi açtığınızda hem sistem genelinde hem de uygulama başına geri kazanma işlemini görebilirsiniz:

Perfetto'da doğrudan ve memcg geri kazanımı gösteriliyor

İzde aşağıdakileri arayın:

  • kswapd: Kernel iş parçacıkları altında kswapd0 simgesini arayın (yukarıdaki ekran görüntüsünde manuel olarak en üste sabitlenmiştir). Sistem boş sayfalar bulmakta zorlandığı için bu alanın uyandığını ve çalıştığını (yeşil dilimler) görürsünüz.
  • Doğrudan geri alma: com.android.memorylab işlem ileti dizilerine bakın. İş parçacığının planlama izinin hemen altında mor ftrace etkinlik dilimleri (ör. mm_vmscan_direct_reclaim_begin) gösterilir. Bu, uygulamanın iş parçacığının, çekirdeğin sayfaları boşaltmasını beklerken duraklatıldığını gösterir.
  • RSS ve Değişim Sayaçları:
    • mem.rss.anon: Tahsis düğmelerine dokundukça artar.
    • mem.swap: kswapd ve uygulamanın kendi iş parçacıkları, bu anonim sayfaları ZRAM'e sıkıştırdıkça (doğrudan geri alma işlemine tabidir) kademeli olarak artar.
  • memcg Reclaim: Manuel geri kazanmayı tetiklediğiniz ana yakınlaştırırsanız rss.anon ve rss.file değerlerinde keskin bir düşüşün yanı sıra mm_vmscan_memcg_reclaim etkinliklerini görürsünüz.

← Sistem genelinde | ↑ Yukarı | kswapd ve lmkd etkileşimi →