Pengklaiman kembali memori: penghapusan dan swap

Di bagian sebelumnya, kita telah melihat cara kernel mengelola sistem memori di seluruh sistem. Dalam bab ini, kita akan membahas lebih dalam Grup Kontrol Memori (memcg), mekanisme yang digunakan Android untuk mempartisi dan mengontrol penggunaan memori untuk setiap aplikasi.

Memahami memcg sangat penting karena di sinilah sistem dapat menargetkan setiap aplikasi untuk mengklaim kembali atau menetapkan batas memori, sehingga memungkinkan pengelolaan jejak memori aplikasi secara proaktif.

Grup kontrol memori (memcg)

Grup Kontrol (cgroup) adalah fitur kernel Linux yang memungkinkan proses diatur ke dalam grup hierarkis dan mendistribusikan resource sistem (seperti CPU, memori, dan I/O) di antara grup tersebut. memcg adalah pengontrol cgroup khusus untuk memori.

Konsep utama memcg

  • Memcg Charge: Saat proses dalam memcg mengalokasikan halaman memori (anonim atau yang didukung file), halaman tersebut akan "dikenai biaya" ke memcg. Total biaya memcg adalah jumlah semua halaman yang digunakan oleh semua proses di dalamnya.
  • Akuntansi Hierarkis: Penggunaan memori dihitung hingga ke atas hierarki. Biaya dalam cgroup turunan juga dihitung terhadap penggunaan induknya.
  • Batas Memori: Setiap memcg dapat memiliki batas (seperti memory.max atau memory.high) yang memicu klaim kembali atau bahkan OOM killer jika terlampaui, terlepas dari memori sistem global.
  • Klaim Kembali Per-memcg: Saat memcg melebihi batasnya, atau saat sistem memerlukan memori, kernel dapat menargetkan memcg tertentu untuk klaim kembali. Artinya, mengeluarkan halaman filenya atau menukar halaman anonimnya ke ZRAM.

Hierarki memcg Android

Android menggunakan hierarki tertentu untuk mengelola proses aplikasi. Struktur ini memungkinkan sistem menerapkan kebijakan yang berbeda ke berbagai jenis aplikasi (misalnya, latar depan vs. latar belakang).

Hierarki memcg Android

  • /sys/fs/cgroup/apps/: Root untuk semua aplikasi Android.
  • uid_<UID>/: Direktori untuk setiap ID Pengguna aplikasi. Semua proses yang termasuk dalam paket aplikasi yang sama berbagi grup ini.
  • pid_<PID>/: Direktori untuk setiap proses individual dan turunan yang di- fork darinya. Hal ini memungkinkan kontrol dan akuntansi yang terperinci untuk aplikasi dengan beberapa proses.

Latihan interaktif: menjelajahi memcg

Dalam latihan ini, Anda akan menemukan direktori memcg untuk MemoryLab dan mengamati biaya memorinya secara real-time.

1. Meluncurkan MemoryLab

Pastikan MemoryLab berjalan di perangkat Anda.

2. Menemukan memcg MemoryLab

Pertama, dapatkan PID aplikasi MemoryLab yang sedang berjalan:

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

Sekarang, temukan direktori cgroup-nya. Anda dapat menemukannya di /proc/<PID>/cgroup:

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

Di cgroup v2, jalur setelah 0:: mewakili hierarki memcg relatif terhadap /sys/fs/cgroup. Jadi, jalur lengkapnya adalah /sys/fs/cgroup/apps/uid_10274/pid_11672/.

3. Membaca statistik memcg

Buka direktori tersebut dan lihat file kunci:

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

memory.current menampilkan total jumlah memori (dalam byte) yang saat ini dikenai biaya ke cgroup ini.

# 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 memberikan perincian: * anon: Jumlah memori anonim (heap, stack). * file: Jumlah memori yang didukung file (cache halaman). * swap: Jumlah memori yang ditukar ke ZRAM.

4. Mengamati perubahan

  1. Buka MemoryLab.
  2. Perhatikan nilai memory.current.
  3. Ketuk Allocate Java Memory (10MB) beberapa kali.
  4. Baca memory.current lagi. Anda akan melihatnya bertambah sekitar 10 MB untuk setiap ketukan.
  5. Ketuk Allocate Bitmaps. Periksa memory.stat untuk melihat peningkatan anon.

Klaim kembali proaktif dengan memory.reclaim

Fitur memcg (v2) yang canggih adalah file memory.reclaim. Menulis nilai ke file ini akan menginstruksikan kernel untuk segera mencoba mengklaim kembali jumlah memori tersebut dari memcg ini dan memcg apa pun di bawahnya.

Cara Android menggunakan memory.reclaim

CachedAppOptimizer Android menggunakan fitur ini untuk mengklaim kembali memori secara maksimal dari aplikasi setelah dibekukan. App Freezer Android memastikan aplikasi yang di-cache menggunakan RAM sesedikit mungkin saat tidak berjalan. Saat aplikasi berpindah ke latar belakang dan dibekukan, sistem akan menulis penggunaan memori aplikasi saat ini ke dalam file memory.reclaim. Hal ini memaksa kernel untuk mengeluarkan semua halaman file yang memungkinkan dan menukar semua halaman anonim ke ZRAM, sehingga meminimalkan jejak residen aplikasi.

Anda dapat mencapai "klaim kembali maksimal" yang sama secara manual dengan membaca memory.current dan menuliskannya kembali ke memory.reclaim:

# 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"

Latihan: memaksa klaim kembali dan pelacakan

Sekarang kita akan memaksa kernel untuk mengklaim kembali memori dari MemoryLab dan merekam aktivitas dalam rekaman aktivitas Perfetto.

  1. Persiapan: Pastikan MemoryLab memiliki beberapa alokasi (Java dan Bitmap).
  2. Mulai Pelacakan: Gunakan konfigurasi pelacakan inline ini untuk merekam peristiwa penjadwalan, klaim kembali, dan cache halaman.

    # 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. Memicu Tekanan dan Klaim Kembali:

    • Di MemoryLab, ketuk Allocate Native Memory (1GB). Tindakan ini akan memicu tekanan di seluruh sistem.
    • Di terminal terpisah, klaim kembali 200 MB:

      adb shell "echo 200M > /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.reclaim"
      
  4. Kesalahan Kembali: Beralih kembali ke MemoryLab. Ketuk Thrash Pagecache (Refault test).

  5. Hentikan Pelacakan: Tekan Ctrl+C di terminal pelacakan.

Menganalisis klaim kembali di Perfetto

Saat membuka rekaman aktivitas, Anda dapat melihat klaim kembali di seluruh sistem dan per aplikasi:

Perfetto menampilkan klaim ulang memcg dan langsung

Cari hal berikut dalam rekaman aktivitas:

  • kswapd: Cari kswapd0 di bagian Kernel threads (dalam screenshot di atas, item ini disematkan secara manual di bagian atas). Anda akan melihatnya aktif dan berjalan (irisan hijau) saat sistem berupaya menemukan halaman kosong.
  • Klaim Kembali Langsung: Lihat thread proses com.android.memorylab. Anda akan melihat irisan peristiwa ftrace berwarna ungu (seperti mm_vmscan_direct_reclaim_begin) yang muncul langsung di bawah jalur penjadwalan thread. Hal ini menunjukkan bahwa thread aplikasi itu sendiri terhenti menunggu kernel mengosongkan halaman.
  • Penghitung RSS dan Swap:
    • mem.rss.anon: Meningkat saat Anda mengetuk tombol alokasi.
    • mem.swap: Meningkat secara stabil saat kswapd dan thread aplikasi sendiri (yang tunduk pada klaim kembali langsung) mengompresi halaman anonim tersebut ke ZRAM.
  • Klaim Kembali memcg: Jika memperbesar momen saat Anda memicu klaim kembali manual, Anda akan melihat penurunan tajam pada rss.anon dan rss.file, disertai dengan peristiwa mm_vmscan_memcg_reclaim.

← Seluruh sistem | ↑ Atas | Interaksi kswapd dan lmkd →