मेमोरी लिमिटर

Android 17 और इसके बाद के वर्शन में, मेमोरी लिमिटर की सुविधा शामिल है. यह एक सिस्टम सर्विस है, जो Linux cgroup v2 का इस्तेमाल करके, ऐप्लिकेशन प्रोसेस की मेमोरी के इस्तेमाल को मॉनिटर और सीमित करती है. मेमोरी लिमिटर, अलग-अलग ऐप्लिकेशन को सिस्टम की मेमोरी का ज़रूरत से ज़्यादा इस्तेमाल करने से रोकता है. इससे मेमोरी पर पड़ने वाला कुल दबाव कम होता है. साथ ही, फ़ोरग्राउंड और कैश मेमोरी में सेव की गई ज़रूरी प्रोसेस को कम मेमोरी की वजह से बंद होने से रोका जा सकता है.

मैकेनिज़्म

मेमोरी लिमिटर, ऐक्टिविटी मैनेजर सर्विस (एएमएस) के साथ इंटिग्रेट होता है, ताकि प्रोसेस लाइफ़साइकल इवेंट और स्थिति में होने वाले बदलावों को ट्रैक किया जा सके. मेमोरी लिमिटर, Linux kernel cgroup v2 हैरारकी का इस्तेमाल करके, मेमोरी की सीमाएं लागू करता है.

मेमोरी लिमिटर का इस्तेमाल करने के लिए, डिवाइस के कर्नेल में cgroup v2 और memory कंट्रोलर चालू होना चाहिए. यह सेवा खास तौर पर इन एट्रिब्यूट पर निर्भर करती है:

memory.high
सॉफ़्ट लिमिट. इस सीमा को पार करने पर, प्रोसेस को धीमा कर दिया जाता है और कर्नेल, उस प्रोसेस से पेज को वापस पाने की कोशिश करता है.
memory.swap.max
यह प्रोसेस के लिए, जगह बनाने की ज़्यादा से ज़्यादा सीमा (जैसे, ZRAM) तय करता है.

ऐप्लिकेशन पर असर

मेमोरी लिमिटर का असर उन ऐप्लिकेशन पर नहीं पड़ता जो मेमोरी की तय सीमा के अंदर काम करते हैं.

जब कोई ऐप्लिकेशन memory.high की सीमा को पार कर जाता है, तो कर्नेल, ऐप्लिकेशन की क्लीन फ़ाइल-बैक्ड मेमोरी को हटा देता है. साथ ही, ऐप्लिकेशन को सीमा के अंदर रखने के लिए, उसकी इनऐक्टिव एनॉनिमस मेमोरी को स्वैप कर देता है. पेज को हटाने और स्वैप करने की इस गतिविधि की वजह से, ऐप्लिकेशन को कुछ समय के लिए एक्ज़ीक्यूशन थ्रॉटलिंग का सामना करना पड़ सकता है. हालांकि, यह चलता रहता है.

कुछ मामलों में, ऐप्लिकेशन मेमोरी को ऐलोकेट नहीं कर पाता और बंद हो जाता है. जैसे, मेमोरी लीक होने की समस्या को कंट्रोल न कर पाना. इसमें, स्वैप स्पेस खत्म होने के बाद भी ऐप्लिकेशन, अज्ञात मेमोरी को ऐलोकेट करता रहता है.

प्रोसेस मॉनिटरिंग

मेमोरी लिमिटर, डिफ़ॉल्ट रूप से ऐप्लिकेशन की प्रोसेस (यूआईडी >= 10000) को मॉनिटर करता है. सिस्टम की मुख्य प्रोसेस को छूट दी गई है, ताकि सिस्टम की ज़रूरी स्थिरता बनी रहे.

मेमोरी लिमिटर, प्रोसेस के दिखने की स्थिति के आधार पर मेमोरी की सीमाएं तय करता है:

  • दिखने वाली प्रोसेस: ऐसी प्रोसेस जो उपयोगकर्ता को दिखने वाले यूज़र इंटरफ़ेस (यूआई) को होस्ट करती हैं. जैसे, फ़िलहाल फ़ोकस की गई गतिविधि या कोई ऐक्टिव इंटरैक्शन. यूज़र इंटरफ़ेस (यूआई) दिखाने के लिए, रेंडरिंग और ग्राफ़िक पाइपलाइन के लिए ज़्यादा बड़े वर्किंग सेट की ज़रूरत होती है. इसलिए, विज़िबल प्रोसेस को ज़्यादा मेमोरी मिलती है.

  • न दिखने वाली प्रोसेस: ऐसी प्रोसेस जो यूज़र इंटरफ़ेस (यूआई) दिखाए बिना बैकग्राउंड में काम करती हैं. जैसे, बैकग्राउंड में चलने वाली सेवाएं या ब्रॉडकास्ट रिसीवर. ये यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट रेंडर नहीं करते हैं. इसलिए, ये ज़्यादा पाबंदियों के साथ काम करते हैं.

नीचे दी गई टेबल में, प्रोसेस की खास स्थितियों को मेमोरी की सीमा के हिसाब से कैटगरी में बांटा गया है:

प्रोसेस की स्थितिमेमोरी की सीमा
PERSISTENTअप्रतिबंधित
PERSISTENT_UIअप्रतिबंधित
TOPज़्यादा लोगों को दिखे
BOUND_TOPज़्यादा लोगों को दिखे
FOREGROUND_SERVICEनहीं दिखेगा
BOUND_FOREGROUND_SERVICEनहीं दिखेगा
IMPORTANT_FOREGROUNDज़्यादा लोगों को दिखे
IMPORTANT_BACKGROUNDनहीं दिखेगा
TRANSIENT_BACKGROUNDनहीं दिखेगा
BACKUPनहीं दिखेगा
SERVICEनहीं दिखेगा
RECEIVERनहीं दिखेगा
TOP_SLEEPINGज़्यादा लोगों को दिखे
HEAVY_WEIGHTनहीं दिखेगा
HOMEनहीं दिखेगा
LAST_ACTIVITYनहीं दिखेगा
CACHED_ACTIVITYसंचित
CACHED_ACTIVITY_CLIENTसंचित
CACHED_RECENTसंचित
CACHED_EMPTYसंचित

कैश मेमोरी में सेव की गई स्थिति में, प्रोसेस फ़्रीज़ हो जाती हैं और फिर ज़्यादा से ज़्यादा प्रोसेस को वापस लाया जाता है.

जब कोई प्रोसेस, असाइन की गई memory.high की सीमा को पार कर जाती है, तो मेमोरी लिमिटर इवेंट का पता लगाता है. साथ ही, गड़बड़ी का पता लगाने वाली कार्रवाइयां ट्रिगर कर सकता है. जैसे, मेमोरी प्रोफ़ाइल कैप्चर करना या statsd में किसी गड़बड़ी को लॉग करना.

कॉन्फ़िगरेशन

Android 17 और इसके बाद के वर्शन में, मेमोरी लिमिटर को कॉन्फ़िगर करने के लिए, सिस्टम पार्टिशन पर मौजूद स्टैंडर्ड प्लैटफ़ॉर्म कॉन्फ़िगरेशन फ़ाइल का इस्तेमाल किया जाता है. यह फ़ाइल /system/etc/memory-limiter-config.xml पर मौजूद होती है.

एक्सएमएल फ़ॉर्मैट

कॉन्फ़िगरेशन फ़ाइल, memory-limiter-config.xsd में बताए गए स्कीमा के मुताबिक होती है. इस फ़ाइल में, उपलब्ध मेमोरी के हिसाब से तय की गई कई सीमाओं के सेट होते हैं. सेवा, डिवाइस की उपलब्ध रैम के हिसाब से सबसे ज़्यादा मैच होने वाली सीमा का सेट चुनती है. मेमोरी की सभी वैल्यू, मेबीबाइट (एमआईबी) में दी जाती हैं.

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <!-- RAM boundaries account for MemTotal excluding carve-outs. -->
    <limitSet>
      <!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
      <memVisible>10240</memVisible>
      <memNotVisible>5120</memNotVisible>
      <swapVisible>5120</swapVisible>
      <swapNotVisible>5120</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
      <minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
      <memVisible>5120</memVisible>
      <memNotVisible>3072</memNotVisible>
      <swapVisible>3072</swapVisible>
      <swapNotVisible>3072</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
      <minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
      <memVisible>4096</memVisible>
      <memNotVisible>2048</memNotVisible>
      <swapVisible>2048</swapVisible>
      <swapNotVisible>2048</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
      <minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
      <memVisible>2048</memVisible>
      <memNotVisible>1024</memNotVisible>
      <swapVisible>1024</swapVisible>
      <swapNotVisible>1024</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
कॉन्फ़िगरेशन वर्शन की पहचान करने वाला पॉज़िटिव इंटीजर. यह 1 होना चाहिए.
minimumRequiredMemTotal

इस सीमा को लागू करने के लिए, सिस्टम की कम से कम ज़रूरी शर्तें MemTotal (एमआईबी में, इसमें फ़्लोर शामिल है). यह सेवा, /proc/meminfo में कर्नल से मिली कुल उपलब्ध मेमोरी के हिसाब से इसका आकलन करती है.

मेमोरी के कार्व-आउट और सीमाओं को समझना: कर्नल MemTotal की वैल्यू, उस फ़िज़िकल डीआरएएम को दिखाती है जिसे ऑपरेटिंग सिस्टम, हार्डवेयर और फ़र्मवेयर मेमोरी के कार्व-आउट को हटाने के बाद ऐक्सेस कर सकता है. कार्व-आउट, जीपीयू, बेसबैंड मॉडेम, कैमरा आईएसपी, सुरक्षित तरीके से काम करने वाले एनवायरमेंट, और हाइपरवाइज़र जैसे खास हार्डवेयर कॉम्पोनेंट के लिए रैम रिज़र्व रखते हैं. आम तौर पर, ये कॉम्पोनेंट 500 MiB से लेकर 2 GiB से ज़्यादा फ़िज़िकल मेमोरी का इस्तेमाल करते हैं.

MemTotal, डिवाइस के लिए विज्ञापन में बताई गई फ़िज़िकल डीआरएएम से कम है. इसलिए, minimumRequiredMemTotal थ्रेशोल्ड में इन कार्व-आउट के लिए मार्जिन शामिल होता है. उदाहरण के लिए, 16 जीबी रैम वाले डिवाइसों को टारगेट करने वाले कॉन्फ़िगरेशन में, 16384 (16 जीबी) के बजाय 14336 (14 जीबी) की कम से कम सीमा तय की जाती है. वहीं, 12 जीबी रैम वाले डिवाइसों के लिए, 9216 (9 जीबी) की कम से कम सीमा तय की जाती है. इससे यह पक्का होता है कि हर डिवाइस, अपने तय किए गए टियर से मेल खाता हो. भले ही, बोर्ड-लेवल पर कार्व-आउट में अंतर हो.

maximumAllowedMemTotal

इस सीमा को लागू करने के लिए, सिस्टम का ज़्यादा से ज़्यादा MemTotal (एमआईबी में, एक्सक्लूसिव सीलिंग). अगर MemTotal इस सीमा से ज़्यादा या इसके बराबर है, तो सेट की गई सीमा लागू नहीं होती. जिन डिवाइसों में 18,432 MiB या इससे ज़्यादा मेमोरी है या जिन डिवाइसों में 3,200 MiB से कम मेमोरी है उनके लिए, मेमोरी लिमिटर कोई सीमा लागू नहीं करता. इससे प्रोसेस बिना किसी पाबंदी के चलती रहती हैं.

memVisible

दिखने वाली प्रोसेस पर लागू की गई एमआईबी में सॉफ़्ट मेमोरी की सीमा (memory.high).

memNotVisible

एमआईबी में सॉफ़्ट मेमोरी की सीमा (memory.high), न दिखने वाली प्रोसेस पर लागू होती है.

swapVisible

दिखने वाली प्रोसेस पर लागू की गई, MiB में स्वैप स्पेस की सीमा (memory.swap.max).

swapNotVisible

एमआईबी में स्वैप स्पेस की सीमा (memory.swap.max), न दिखने वाली प्रोसेस पर लागू होती है.

स्टैंडर्ड प्लैटफ़ॉर्म की सीमाएं

Android 17 और इसके बाद के वर्शन में, प्लैटफ़ॉर्म पर स्टैंडर्ड मेमोरी और स्वैप की सीमाएं /system/etc/memory-limiter-config.xml में तय की जाती हैं. ये सीमाएं, [floor, ceiling) रेंज इंटरवल का इस्तेमाल करके, सामान्य फ़िज़िकल रैम के टियर के हिसाब से तय की जाती हैं:

टारगेट फ़िज़िकल रैम MemTotal रेंज [फ़्लोर, सीलिंग)
(minimumRequiredMemTotal,
maximumAllowedMemTotal)
उपलब्ध मेमोरी की सीमा
(memVisible)
मेमोरी की सीमा नहीं दिख रही है
(memNotVisible)
स्वैप करने की सीमा
(swapVisible)
प्रॉडक्ट न दिखने की वजह से, स्वैप करने की सीमा
(swapNotVisible)
16 जीबी [14336, 18432) एमआईबी 10,240 एमआईबी (10 जीआईबी) 5,120 एमआईबी (5 जीआईबी) 5,120 एमआईबी (5 जीआईबी) 5,120 एमआईबी (5 जीआईबी)
12 जीबी [9216, 14336) एमआईबी 8,192 एमआईबी (8 जीआईबी) 4,096 एमआईबी (4 जीआईबी) 4,096 एमआईबी (4 जीआईबी) 4,096 एमआईबी (4 जीआईबी)
8 जीबी [6800, 9216) एमआईबी 5,120 एमआईबी (5 जीआईबी) 3,072 एमआईबी (3 जीआईबी) 3,072 एमआईबी (3 जीआईबी) 3,072 एमआईबी (3 जीआईबी)
6 जीबी [4800, 6800) एमआईबी 4,096 एमआईबी (4 जीआईबी) 2,048 एमआईबी (2 जीआईबी) 2,048 एमआईबी (2 जीआईबी) 2,048 एमआईबी (2 जीआईबी)
4 जीबी [3200, 4800) एमआईबी 2,048 एमआईबी (2 जीआईबी) 1,024 एमआईबी (1 जीआईबी) 1,024 एमआईबी (1 जीआईबी) 1,024 एमआईबी (1 जीआईबी)

मेमोरी की सीमा से जुड़े सिद्धांत

मेमोरी लिमिटर कॉन्फ़िगरेशन को प्लैटफ़ॉर्म के इन सिद्धांतों के हिसाब से डिज़ाइन किया गया है:

  • ईकोसिस्टम में एक जैसा अनुभव और ऐप्लिकेशन के साथ काम करना: सभी डिवाइसों पर मेमोरी की एक जैसी सीमाएं होने से, Android ईकोसिस्टम में ऐप्लिकेशन की परफ़ॉर्मेंस का अनुमान लगाया जा सकता है. ऐप्लिकेशन को मेमोरी की स्टैंडर्ड ज़रूरतों के हिसाब से डेवलप और टेस्ट किया जाता है. प्लैटफ़ॉर्म की सीमाओं को एक जैसा बनाए रखने से, ऐप्लिकेशन के अचानक बंद होने या मेमोरी की कमी की वजह से परफ़ॉर्मेंस में गिरावट आने से बचा जा सकता है.

  • संसाधनों का अनुपात के हिसाब से बंटवारा: प्लैटफ़ॉर्म की सीमाएं, फ़िज़िकल रैम की क्षमता के हिसाब से तय की जाती हैं:

    • दिखने वाली प्रोसेस: ऐक्टिव यूज़र इंटरफ़ेस (यूआई), कंपोज़िशन, और रेंडरिंग वर्कलोड के लिए, कुल फ़िज़िकल रैम का करीब आधा से दो-तिहाई हिस्सा असाइन किया गया है.
    • न दिखने वाली प्रोसेस: बैकग्राउंड टास्क के लिए, कुल फ़िज़िकल रैम का करीब 1/4 से 1/3 हिस्सा असाइन किया जाता है.
  • सभी पर लागू: ये सीमाएं, डिवाइस पर मौजूद सभी ऐप्लिकेशन प्रोसेस (यूआईडी >= 10000) पर एक समान रूप से लागू होती हैं. इनमें पहले से इंस्टॉल किए गए सिस्टम और ओईएम ऐप्लिकेशन भी शामिल हैं. मेमोरी लिमिटर, अनुमति वाली सूचियों के साथ काम नहीं करता. इससे कुछ ऐप्लिकेशन को छूट नहीं दी जा सकती. इससे यह पक्का होता है कि सभी ऐप्लिकेशन के लिए मेमोरी का सही तरीके से इस्तेमाल किया जाए. ज़रूरी इंटरैक्टिव डीमन पर, खुद ट्यून होने वाले memory.min प्रोटेक्शन फ़्लोर को कॉन्फ़िगर करने और सिस्टम के अलग-अलग ऐप्लिकेशन और सेवाओं पर, वर्कलोड के हिसाब से कैलिब्रेट की गई memory.high सीलिंग को कॉन्फ़िगर करने के लिए, सिस्टम मेमोरी बजट देखें.

  • रनटाइम क्वेरी एपीआई की अनुपलब्धता: Android 17 और इसके बाद के वर्शन में, ऐप्लिकेशन रनटाइम के दौरान, प्रोग्राम के हिसाब से मेमोरी के इस्तेमाल की असाइन की गई सीमाओं के बारे में क्वेरी नहीं कर सकते. प्लैटफ़ॉर्म की स्टैंडर्ड सीमाएं, ज़रूरत से ज़्यादा मार्जिन के साथ तय की जाती हैं. इससे, सामान्य इस्तेमाल के दौरान, सही तरीके से काम करने वाले ऐप्लिकेशन बिना किसी पाबंदी के काम करते हैं.

  • कर्नेल रिक्लेम और स्वैप डाइनैमिक्स: जब कोई ऐप्लिकेशन प्रोसेस अपनी memory.high सीमा के करीब पहुंचती है, तो Linux कर्नेल खास तौर पर उस प्रोसेस के सीजीग्रुप पर मेमोरी रिक्लेम को डायरेक्ट करता है. इसमें, फ़ाइल-बैक्ड पेजों को हटाना और बिना सोर्स फ़ाइल वाले पेजों को ZRAM में स्वैप करना शामिल है. इससे सिस्टम की परफ़ॉर्मेंस बेहतर बनी रहती है और मेमोरी पर ज़्यादा दबाव नहीं पड़ता.

शेल कमांड

am memory-limiter कमांड की मदद से, डेवलपर और सिस्टम इंटिग्रेटर, डेवलपमेंट, टेस्टिंग, और डीबग करने के लिए, रन टाइम के दौरान सेवा के साथ इंटरैक्ट कर सकते हैं:

am memory-limiter <SUB-COMMAND>

स्थिति

status सब-कमांड, मेमोरी लिमिटर की ऑपरेशनल स्थिति और ऐक्टिव मेट्रिक की रिपोर्ट करता है:

adb shell am memory-limiter status

यहां आउटपुट का उदाहरण दिया गया है:

Memory limiter
  enabled                  monitoring=true          ignored=none
  visibleMem=1948MB        visibleSwap=974MB
  notVisibleMem=974MB      notVisibleSwap=487MB
  started=36               watched=36               watch-failed=0
  events=0                 processes=36             process-hwm=36

आउटपुट के मुख्य फ़ील्ड में ये शामिल हैं:

monitoring
इससे पता चलता है कि मेमोरी लिमिटर, प्रोसेस पर नज़र रख रहा है या नहीं.
visibleMem और notVisibleMem
हर विज़िबिलिटी स्टेट के लिए, मेमोरी की तय की गई सीमा (memory.high) लागू की गई है.
visibleSwap और notVisibleSwap
हर विज़िबिलिटी स्टेट के लिए, फ़िलहाल लागू की गई स्वैप की तय सीमाएं (memory.swap.max).
events
कोई प्रोसेस, असाइन की गई सीमा को कितनी बार पार कर चुकी है.
processes
मॉनिटर की जा रही प्रोसेस की मौजूदा संख्या.

ignore

ignore सब-कमांड, किसी खास यूआईडी या सभी प्रोसेस को मेमोरी की सीमा से कुछ समय के लिए बाहर रखता है. यह परफ़ॉर्मेंस की तुलना करने, स्ट्रेस टेस्टिंग करने या मेमोरी के व्यवहार का विश्लेषण करने के लिए मददगार है:

# Ignore a specific UID
adb shell am memory-limiter ignore 10087
# Ignore all processes (temporarily disables limiting)
adb shell am memory-limiter ignore all
# Resume normal limiting operation
adb shell am memory-limiter ignore none

मैन्युअल

manual सब-कमांड, प्रोसेस आईडी (पीआईडी) के हिसाब से किसी खास प्रोसेस के लिए तय की गई सीमाओं को बदल देता है. इसके लिए, बाइट में कस्टम वैल्यू का इस्तेमाल किया जाता है. वैल्यू एक पूर्णांक होनी चाहिए. हालांकि, इसमें एमआईबी दिखाने के लिए MB सफ़िक्स या जीआईबी दिखाने के लिए GB सफ़िक्स शामिल किया जा सकता है:

# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1073741824
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1024MB
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1GB
# Remove the manual override for PID 1234
adb shell am memory-limiter manual 1234 none

मैन्युअल तरीके से किए गए बदलाव, सिर्फ़ उस प्रोसेस इंस्टेंस के लाइफ़टाइम के लिए लागू होते हैं. अगर प्रोसेस फिर से शुरू होती है, तो यह अपनी स्थिति के आधार पर स्टैंडर्ड सीमाओं पर वापस आ जाती है.

मैन्युअल तरीके से बदलाव करने पर, सिस्टम की तय सीमा से ज़्यादा बदलाव नहीं किया जा सकता.