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 की सीमा से ज़्यादा मेमोरी इस्तेमाल करती है, तो मेमोरी लिमिटर उस इवेंट का पता लगाता है. साथ ही, मेमोरी प्रोफ़ाइल कैप्चर करने या statsd में गड़बड़ी लॉग करने जैसी डाइग्नोस्टिक कार्रवाइयां ट्रिगर कर सकता है.
कॉन्फ़िगरेशन
Android 17 और इसके बाद के वर्शन में, मेमोरी लिमिटर, सिस्टम पार्टिशन पर प्लैटफ़ॉर्म की डिफ़ॉल्ट कॉन्फ़िगरेशन फ़ाइल उपलब्ध कराता है. साथ ही, vendor पार्टिशन पर डिवाइस के हिसाब से, वैकल्पिक तौर पर बदलाव करने की सुविधा भी देता है:
- सिस्टम की डिफ़ॉल्ट कॉन्फ़िगरेशन:
/system/etc/memory-limiter-config.xml - वेंडर के हिसाब से बदलाव (ज़रूरी नहीं):
/vendor/etc/memory-limiter-config.xml
मेमोरी लिमिटर, सबसे पहले /vendor/etc/memory-limiter-config.xml की जांच करता है.
अगर यह फ़ाइल मौजूद है, तो यह वेंडर के कॉन्फ़िगरेशन का इस्तेमाल करता है. ऐसा न होने पर, यह /system/etc/memory-limiter-config.xml में मौजूद सिस्टम के डिफ़ॉल्ट कॉन्फ़िगरेशन पर वापस आ जाता है.
एक्सएमएल फ़ॉर्मैट
कॉन्फ़िगरेशन फ़ाइल, memory-limiter-config.xsd में तय किए गए स्कीमा के मुताबिक होती है. इस फ़ाइल में, उपलब्ध मेमोरी के हिसाब से क्रम में लगाए गए, कई लिमिट सेट तय किए जाते हैं. सर्विस, डिवाइस में उपलब्ध रैम के आधार पर, सबसे ज़्यादा मैच करने वाला लिमिट सेट चुनती है. मेमोरी की सभी वैल्यू, मेबिबाइट (एमआईबी) में तय की जाती हैं.
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM minimums account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- यह एक पॉज़िटिव इंटीजर है, जो कॉन्फ़िगरेशन के वर्शन की पहचान करता है. इसकी वैल्यू
1होनी चाहिए. minimumRequiredMemTotalसिस्टम का वह कम से कम
MemTotal(एमआईबी में), जिसकी ज़रूरत इस लिमिट सेट को लागू करने के लिए होती है. सर्विस, इसकी तुलना/proc/meminfoमें कर्नल की ओर से रिपोर्ट की गई, उपलब्ध कुल मेमोरी से करती है.मेमोरी कार्व-आउट के बारे में जानकारी: कर्नल की
MemTotalवैल्यू, हार्डवेयर और फ़र्मवेयर मेमोरी कार्व-आउट घटाने के बाद, ऑपरेटिंग सिस्टम के लिए उपलब्ध फ़िज़िकल DRAM को दिखाती है. कार्व-आउट, रैम को खास हार्डवेयर कॉम्पोनेंट के लिए रिज़र्व करते हैं. जैसे, जीपीयू, बेसबैंड मॉडेम, कैमरा आईएसपी, सुरक्षित एक्ज़ीक्यूशन एनवायरमेंट, और हाइपरवाइज़र. आम तौर पर, ये 500 एमआईबी से लेकर 1 जीबी से ज़्यादा फ़िज़िकल मेमोरी का इस्तेमाल करते हैं.MemTotal, डिवाइस के विज्ञापन में दिखाई गई फ़िज़िकल DRAM से कम होता है.minimumRequiredMemTotalकी थ्रेशोल्ड में इन कार्व-आउट के लिए मार्जिन शामिल होता है. उदाहरण के लिए, 16 जीबी वाले डिवाइसों के लिए कॉन्फ़िगरेशन में,minimumRequiredMemTotalकी वैल्यू16384(16 जीबी) के बजाय13312(13 जीबी) तय की जाती है. वहीं, 12 जीबी वाले डिवाइसों के लिए, इसकी वैल्यू10240(10 जीबी) तय की जाती है. इससे यह पक्का होता है कि हर डिवाइस, बोर्ड-लेवल के कार्व-आउट में होने वाले बदलावों के बावजूद, अपने तय किए गए टियर से मैच करे.memVisibleयह दिखने वाली प्रोसेस पर लागू होने वाली, सॉफ़्ट मेमोरी लिमिट (
memory.high) है. इसकी वैल्यू एमआईबी में होती है.memNotVisibleयह नहीं दिखने वाली प्रोसेस पर लागू होने वाली, सॉफ़्ट मेमोरी लिमिट (
memory.high) है. इसकी वैल्यू एमआईबी में होती है.swapVisibleयह दिखने वाली प्रोसेस पर लागू होने वाली, स्वैप स्पेस लिमिट (
memory.swap.max) है. इसकी वैल्यू एमआईबी में होती है.swapNotVisibleयह नहीं दिखने वाली प्रोसेस पर लागू होने वाली, स्वैप स्पेस लिमिट (
memory.swap.max) है. इसकी वैल्यू एमआईबी में होती है.
प्लैटफ़ॉर्म की स्टैंडर्ड सीमाएं
Android 17 और इसके बाद के वर्शन में, प्लैटफ़ॉर्म, /system/etc/memory-limiter-config.xml में, फ़िज़िकल रैम के सामान्य टियर के लिए, मेमोरी और स्वैप की स्टैंडर्ड सीमाएं तय करता है:
| टारगेट फ़िज़िकल रैम |
ज़रूरी MemTotal (minimumRequiredMemTotal) |
दिखने वाली प्रोसेस के लिए मेमोरी की सीमा (memVisible) |
नहीं दिखने वाली प्रोसेस के लिए मेमोरी की सीमा (memNotVisible) |
दिखने वाली प्रोसेस के लिए स्वैप की सीमा (swapVisible) |
नहीं दिखने वाली प्रोसेस के लिए स्वैप की सीमा (swapNotVisible) |
|---|---|---|---|---|---|
| 16 जीबी | 13,312 एमआईबी (13 जीबी) | 10,240 एमआईबी (10 जीबी) | 5,120 एमआईबी (5 जीबी) | 5,120 एमआईबी (5 जीबी) | 5,120 एमआईबी (5 जीबी) |
| 12 जीबी | 10,240 एमआईबी (10 जीबी) | 8,192 एमआईबी (8 जीबी) | 4,096 एमआईबी (4 जीबी) | 4,096 एमआईबी (4 जीबी) | 4,096 एमआईबी (4 जीबी) |
| 8 जीबी | 6,656 एमआईबी (6.5 जीबी) | 5,120 एमआईबी (5 जीबी) | 3,072 एमआईबी (3 जीबी) | 3,072 एमआईबी (3 जीबी) | 3,072 एमआईबी (3 जीबी) |
| 6 जीबी | 4,608 एमआईबी (4.5 जीबी) | 4,096 एमआईबी (4 जीबी) | 2,048 एमआईबी (2 जीबी) | 2,048 एमआईबी (2 जीबी) | 2,048 एमआईबी (2 जीबी) |
| 4 जीबी | 3,072 एमआईबी (3 जीबी) | 2,048 एमआईबी (2 जीबी) | 1,024 एमआईबी (1 जीबी) | 1,024 एमआईबी (1 जीबी) | 1,024 एमआईबी (1 जीबी) |
मेमोरी की सीमा से जुड़े सिद्धांत
मेमोरी लिमिटर का कॉन्फ़िगरेशन, प्लैटफ़ॉर्म के इन सिद्धांतों के आधार पर डिज़ाइन किया गया है:
इकोसिस्टम में एकरूपता और ऐप्लिकेशन की कंपैटिबिलिटी: सभी डिवाइसों पर मेमोरी की सीमाएं एक जैसी होने से, Android इकोसिस्टम में ऐप्लिकेशन की परफ़ॉर्मेंस का अनुमान लगाया जा सकता है. ऐप्लिकेशन, मेमोरी की स्टैंडर्ड उम्मीदों के हिसाब से डेवलप और टेस्ट किए जाते हैं. प्लैटफ़ॉर्म की सीमाओं को एक जैसा बनाए रखने से, अनचाहे थ्रॉटलिंग या समय से पहले बंद होने की समस्याओं से बचा जा सकता है.
संसाधनों का आनुपातिक बंटवारा: प्लैटफ़ॉर्म की सीमाएं, फ़िज़िकल रैम की क्षमता के हिसाब से तय की जाती हैं:
- दिखने वाली प्रोसेस: ऐक्टिव यूज़र इंटरफ़ेस (यूआई), कंपोज़िशन, और रेंडरिंग वर्कलोड को सपोर्ट करने के लिए, कुल फ़िज़िकल रैम का करीब 1/2 से 2/3 हिस्सा असाइन किया जाता है.
- नहीं दिखने वाली प्रोसेस: बैकग्राउंड में चलने वाले टास्क के लिए, कुल फ़िज़िकल रैम का करीब 1/4 से 1/3 हिस्सा असाइन किया जाता है.
सभी पर लागू होना: सीमाएं, डिवाइस पर मौजूद सभी ऐप्लिकेशन प्रोसेस (यूआईडी >= 10000) पर एक जैसी लागू होती हैं. इनमें, पहले से इंस्टॉल किए गए सिस्टम और ओईएम ऐप्लिकेशन भी शामिल हैं. मेमोरी लिमिटर, खास ऐप्लिकेशन को छूट देने के लिए, अनुमति वाली सूचियों के साथ काम नहीं करता. इससे, सभी ऐप्लिकेशन के लिए मेमोरी का सही तरीके से इस्तेमाल किया जा सकता है.
रनटाइम क्वेरी एपीआई मौजूद नहीं हैं: Android 17 और इसके बाद के वर्शन में, ऐप्लिकेशन रन टाइम में, प्रोग्राम के ज़रिए अपनी असाइन की गई मेमोरी की सीमाओं के बारे में क्वेरी नहीं कर सकते. प्लैटफ़ॉर्म की स्टैंडर्ड सीमाएं, काफ़ी मार्जिन के साथ तय की जाती हैं, ताकि सही तरीके से काम करने वाले ऐप्लिकेशन, सामान्य इस्तेमाल के दौरान बिना किसी पाबंदी के काम कर सकें.
कर्नल की ओर से मेमोरी वापस पाने और स्वैप करने की प्रोसेस: जब कोई ऐप्लिकेशन प्रोसेस,
memory.highकी सीमा के आस-पास पहुंचती है, तो Linux कर्नल, मेमोरी को वापस पाने की प्रोसेस को खास तौर पर उस प्रोसेस के cgroup पर डायरेक्ट करता है. इसमें, इस्तेमाल न की जा रही फ़ाइल-बैक मेमोरी वाले पेजों को हटाना और ठंडे एनॉनिमस पेजों को ZRAM में स्वैप करना शामिल है. इससे, सिस्टम की फ़्लूडिटी बनाए रखी जाती है और मेमोरी पर पड़ने वाले कुल दबाव को कम किया जाता है.
Shell कमांड
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
मैन्युअल तौर पर किए गए बदलाव, सिर्फ़ उस खास प्रोसेस इंस्टेंस के लाइफ़टाइम के लिए लागू होते हैं. अगर प्रोसेस रीस्टार्ट होती है, तो यह अपनी स्थिति के आधार पर, स्टैंडर्ड सीमाओं पर वापस आ जाती है.
मैन्युअल तौर पर किया गया बदलाव, सिस्टम की फ़िज़िकल सीमाओं से ज़्यादा नहीं हो सकता.