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

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

मैकेनिज़्म

मेमोरी लिमिटर, ऐक्टिविटी मैनेजर सर्विस (एएमएस) के साथ इंटिग्रेट होता है. इससे प्रोसेस के लाइफ़साइकल इवेंट और स्थिति में होने वाले बदलावों को ट्रैक किया जा सकता है. मेमोरी लिमिटर, Linux कर्नल 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 की सीमा से ज़्यादा हो जाती है, तब Memory Limiter उस इवेंट का पता लगाता है. साथ ही, मेमोरी प्रोफ़ाइल कैप्चर करने या statsd में गड़बड़ी लॉग करने जैसी डाइग्नोस्टिक कार्रवाइयां ट्रिगर कर सकता है.

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

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

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

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

<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 (MiB में, फ़्लोर शामिल है) होना ज़रूरी है. यह सेवा, /proc/meminfo में कर्नल की ओर से रिपोर्ट की गई कुल उपलब्ध मेमोरी के हिसाब से इसका आकलन करती है.

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

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

maximumAllowedMemTotal

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

memVisible

यह MiB में, दिखने वाली प्रोसेस पर लागू होने वाली सॉफ्ट मेमोरी लिमिट (memory.high) है.

memNotVisible

MiB में, न दिखने वाली प्रोसेस पर लागू होने वाली सॉफ्ट मेमोरी लिमिट (memory.high).

swapVisible

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

swapNotVisible

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

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

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

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

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

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

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

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

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

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

  • कर्नेल रीक्लेम और स्वैप डाइनैमिक: जब कोई ऐप्लिकेशन प्रोसेस अपनी memory.high सीमा के करीब पहुंच जाती है, तो Linux कर्नेल, मेमोरी रीक्लेम को खास तौर पर उस प्रोसेस cgroup पर डायरेक्ट करता है. इसमें, फ़ाइल के साथ काम न करने वाले पेजों को हटाना और बिना सोर्स फ़ाइल वाले ठंडे पेजों को 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 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 सब-कमांड, किसी प्रोसेस के लिए कैलकुलेट की गई सीमाओं को बदलती है. इसके लिए, प्रोसेस आईडी (पीआईडी) के साथ बाइट में कस्टम ऐब्सलूट वैल्यू का इस्तेमाल किया जाता है. वैल्यू एक पूर्णांक होनी चाहिए. हालांकि, इसमें MiB दिखाने के लिए MB सफ़िक्स या GiB दिखाने के लिए 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

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

मैन्युअल ओवरराइड, सिस्टम की तय की गई सीमाओं से ज़्यादा नहीं हो सकता.