मेमोरी खाली करना: इविक्ट करना और स्वैप करना

पिछले सेक्शन में, हमने देखा कि कर्नल पूरे सिस्टम में मेमोरी को कैसे मैनेज करता है. इस चैप्टर में, हम मेमोरी कंट्रोल ग्रुप (memcg) के बारे में ज़्यादा जानकारी देंगे. यह एक ऐसा सिस्टम है जिसका इस्तेमाल Android, अलग-अलग ऐप्लिकेशन के लिए मेमोरी के इस्तेमाल को कंट्रोल करने और उसे बांटने के लिए करता है.

memcg को समझना ज़रूरी है, क्योंकि यह वह लेवल है जिस पर सिस्टम, मेमोरी वापस पाने के लिए अलग-अलग ऐप्लिकेशन को टारगेट कर सकता है या उन पर मेमोरी की सीमाएं सेट कर सकता है. इससे ऐप्लिकेशन की मेमोरी के इस्तेमाल को बेहतर तरीके से मैनेज किया जा सकता है.

मेमोरी कंट्रोल ग्रुप (memcg)

कंट्रोल ग्रुप (cgroups), Linux कर्नल की एक सुविधा है. इसकी मदद से, प्रोसेस को क्रमबद्ध ग्रुप में व्यवस्थित किया जा सकता है. साथ ही, सिस्टम के संसाधनों (जैसे कि सीपीयू, मेमोरी, और I/O) को उनके बीच बांटा जा सकता है. memcg, cgroup कंट्रोलर है. यह खास तौर पर मेमोरी के लिए होता है.

memcg के मुख्य कॉन्सेप्ट

  • Memcg Charge: जब memcg में कोई प्रोसेस, मेमोरी का कोई पेज (चाहे वह गुमनाम हो या फ़ाइल-बैक किया गया हो) असाइन करती है, तो उस पेज को memcg के लिए "चार्ज" किया जाता है. किसी memcg का कुल शुल्क, उसमें मौजूद सभी प्रोसेस के इस्तेमाल किए गए सभी पेजों का योग होता है.
  • हैरारकी के हिसाब से हिसाब रखना: मेमोरी के इस्तेमाल का हिसाब, ट्री के हिसाब से रखा जाता है. किसी चाइल्ड ग्रुप में किए गए शुल्क को भी पैरंट ग्रुप के इस्तेमाल में गिना जाता है.
  • मेमोरी की सीमाएं: हर memcg के लिए सीमाएं (जैसे, memory.max या memory.high) सेट की जा सकती हैं. अगर ये सीमाएं पार हो जाती हैं, तो मेमोरी को वापस पाने की प्रोसेस शुरू हो जाती है या OOM किलर ट्रिगर हो जाता है. यह प्रोसेस, ग्लोबल सिस्टम मेमोरी से अलग होती है.
  • Per-memcg Reclaim: जब कोई memcg अपनी सीमा से ज़्यादा हो जाता है या जब सिस्टम को मेमोरी की ज़रूरत होती है, तो कर्नल मेमोरी को वापस पाने के लिए किसी खास memcg को टारगेट कर सकता है. इसका मतलब है कि इसके फ़ाइल पेजों को हटाना या इसके गुमनाम पेजों को ZRAM में स्वैप करना.

Android memcg हैरारकी

Android, ऐप्लिकेशन प्रोसेस को मैनेज करने के लिए एक खास क्रम का इस्तेमाल करता है. इस स्ट्रक्चर की मदद से, सिस्टम अलग-अलग तरह के ऐप्लिकेशन पर अलग-अलग नीतियां लागू कर सकता है. उदाहरण के लिए, फ़ोरग्राउंड बनाम बैकग्राउंड.

Android memcg हैरारकी

  • /sys/fs/cgroup/apps/: यह सभी Android ऐप्लिकेशन के लिए रूट है.
  • uid_<UID>/: हर ऐप्लिकेशन के उपयोगकर्ता आईडी के लिए एक डायरेक्ट्री. एक ही ऐप्लिकेशन पैकेज से जुड़ी सभी प्रोसेस, इस ग्रुप को शेयर करती हैं.
  • pid_<PID>/: हर प्रोसेस और उससे फ़ोर्क किए गए किसी भी चाइल्ड के लिए डायरेक्ट्री. इससे कई प्रोसेस वाले ऐप्लिकेशन के लिए, बेहतर कंट्रोल और अकाउंटिंग की सुविधा मिलती है.

खुद करके देखें: memcg एक्सप्लोर करना

इस गतिविधि में, आपको MemoryLab के लिए memcg डायरेक्ट्री मिलेगी. साथ ही, आपको रीयल-टाइम में इसके मेमोरी चार्ज को मॉनिटर करने का मौका मिलेगा.

1. MemoryLab लॉन्च करें

पक्का करें कि आपके डिवाइस पर MemoryLab चल रहा हो.

2. MemoryLab के memcg का पता लगाना

सबसे पहले, चल रहे MemoryLab ऐप्लिकेशन का पीआईडी पाएं:

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

अब इसकी cgroup डायरेक्ट्री ढूंढें. यह जानकारी /proc/<PID>/cgroup में देखी जा सकती है:

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

सीजी्रुप v2 में, 0:: के बाद का पाथ, /sys/fs/cgroup के हिसाब से memcg के क्रम को दिखाता है. इसलिए, पूरा पाथ /sys/fs/cgroup/apps/uid_10274/pid_11672/ है.

3. मेमोरी कंट्रोल ग्रुप (memcg) के आंकड़े पढ़ने की अनुमति

उस डायरेक्ट्री में जाएं और मुख्य फ़ाइलें देखें:

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

memory.current इस cgroup के लिए, फ़िलहाल चार्ज की गई मेमोरी की कुल रकम (बाइट में) दिखाता है.

# 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 में यह जानकारी दी जाती है: * anon: उपयोगकर्ता की पहचान छिपाकर शेयर की जाने वाली मेमोरी की मात्रा (हीप, स्टैक). * file: फ़ाइल-बैक मेमोरी (पेज कैश) की मात्रा. * swap: ZRAM में स्वैप की गई मेमोरी की मात्रा.

4. बदलावों पर नज़र रखना

  1. MemoryLab खोलें.
  2. memory.current की वैल्यू नोट करें.
  3. Java के लिए मेमोरी (10 एमबी) असाइन करें पर कई बार टैप करें.
  4. memory.current को फिर से पढ़ें. आपको हर टैप पर, इसकी साइज़ में करीब 10 एमबी की बढ़ोतरी दिखेगी.
  5. बिटमैप असाइन करें पर टैप करें. anon में हुई बढ़ोतरी देखने के लिए, memory.stat देखें.

memory.reclaim की मदद से, पहले से ही दावा किए गए ऑफ़र को वापस पाने की सुविधा

memcg (v2) की एक अहम सुविधा, memory.reclaim फ़ाइल है. इस फ़ाइल में कोई वैल्यू लिखने पर, कर्नल को यह निर्देश मिलता है कि वह memcg और इसके तहत आने वाले किसी भी memcg से, उतनी मेमोरी तुरंत वापस पाने की कोशिश करे.

Android, memory.reclaim का इस्तेमाल कैसे करता है

Android का CachedAppOptimizer इस सुविधा का इस्तेमाल करता है, ताकि ऐप्लिकेशन के फ़्रीज़ होने के बाद, ज़्यादा से ज़्यादा मेमोरी वापस पाई जा सके. Android में मौजूद ऐप्लिकेशन फ़्रीज़र यह पक्का करता है कि कैश मेमोरी में सेव किए गए ऐप्लिकेशन, इस्तेमाल न किए जाने पर कम से कम रैम का इस्तेमाल करें. जब कोई ऐप्लिकेशन बैकग्राउंड में चला जाता है और फ़्रीज़ हो जाता है, तो सिस्टम ऐप्लिकेशन की मौजूदा मेमोरी के इस्तेमाल की जानकारी को उसकी memory.reclaim फ़ाइल में लिखता है. इससे कर्नल को सभी फ़ाइल पेजों को हटाने और सभी ऐनॉनमस पेजों को ZRAM में स्वैप करने के लिए मजबूर किया जाता है. इससे ऐप्लिकेशन के मेमोरी फ़ुटप्रिंट को कम किया जा सकता है.

memory.current को पढ़कर और उसे वापस 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"

एक्सरसाइज़: फ़ोर्स रीक्लेम और ट्रेस करना

अब हम कर्नल को MemoryLab से मेमोरी वापस लेने के लिए मजबूर करेंगे और गतिविधि को Perfetto ट्रेस में कैप्चर करेंगे.

  1. तैयारी: पक्का करें कि MemoryLab में कुछ आवंटन (Java और बिटमैप) हों.
  2. ट्रेस करना शुरू करें: इस इनलाइन ट्रेस कॉन्फ़िगरेशन का इस्तेमाल करके, शेड्यूल करने, फिर से पाने, और पेज कैश मेमोरी के इवेंट कैप्चर करें.

    # 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. ट्रिगर प्रेशर और रीक्लेम:

    • MemoryLab में, Allocate Native Memory (1GB) पर टैप करें. इससे पूरे सिस्टम पर दबाव पड़ेगा.
    • किसी दूसरे टर्मिनल में, 200 एमबी वापस पाएं:

      adb shell "echo 200M > /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.reclaim"
      
  4. फ़ॉल्ट बैक इन: MemoryLab पर वापस स्विच करें. Thrash Pagecache (Refault test) पर टैप करें.

  5. ट्रेस बंद करें: ट्रेस टर्मिनल में Ctrl+C दबाएं.

Perfetto में मेमोरी वापस पाने की प्रोसेस का विश्लेषण करना

ट्रेस खोलने पर, आपको सिस्टम-वाइड और हर ऐप्लिकेशन के हिसाब से मेमोरी वापस पाने की सुविधा दिखेगी:

Perfetto में डायरेक्ट और memcg रीक्लेम दिखाया गया है

ट्रेस में ये चीज़ें देखें:

  • kswapd: कर्नेल थ्रेड में जाकर kswapd0 खोजें. ऊपर दिए गए स्क्रीनशॉट में, इसे मैन्युअल तरीके से सबसे ऊपर पिन किया गया है. सिस्टम को मुफ़्त पेज ढूंढने में मुश्किल होने पर, आपको यह चालू होता हुआ और काम करता हुआ (हरे रंग के स्लाइस) दिखेगा.
  • सीधे तौर पर दावा करना: com.android.memorylab प्रोसेस थ्रेड देखें. आपको थ्रेड के शेड्यूलिंग ट्रैक के ठीक नीचे, बैंगनी रंग के ftrace इवेंट स्लाइस (जैसे कि mm_vmscan_direct_reclaim_begin) दिखेंगे. इससे पता चलता है कि ऐप्लिकेशन थ्रेड खुद ही रुक गई है. वह कर्नल के पेजों को खाली करने का इंतज़ार कर रही है.
  • आरएसएस और स्वैप काउंटर:
    • mem.rss.anon: यह तब बढ़ता है, जब एट्रिब्यूशन बटन पर टैप किया जाता है.
    • mem.swap: kswapd और ऐप्लिकेशन के थ्रेड (सीधे तौर पर वापस पाने के लिए) उन बिना सोर्स फ़ाइल वाले पेजों को ZRAM में कंप्रेस करते हैं. इसलिए, यह लगातार बढ़ता रहता है.
  • memcg Reclaim: अगर आपने मैन्युअल तरीके से मेमोरी वापस पाने की सुविधा को ट्रिगर किया है, तो ज़ूम इन करने पर आपको rss.anon और rss.file, दोनों में अचानक गिरावट दिखेगी. साथ ही, mm_vmscan_memcg_reclaim इवेंट भी दिखेंगे.

← पूरे सिस्टम के लिए | ↑ ऊपर की ओर | kswapd और lmkd के बीच इंटरैक्शन →