सिस्टम मेमोरी बजट

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

  • सिस्टम डीमन के लिए सुरक्षा फ़्लोर (SystemMemoryBudgetManager के साथ memory.min): खुद को ट्यून करने वाले cgroup v2 memory.min रिज़र्वेशन (हर डीमन के सेटल किए गए रेज़िडेंट वर्किंग सेट के 90% पर सेट किया गया है और कुल फ़िज़िकल रैम के 10% पर सेट किया गया है), मेमोरी के दबाव में अहम इंटरैक्टिव डीमन और शेल प्रोसेस (system_server, surfaceflinger, ग्राफ़िक्स composer एचएएल, audioserver, cameraserver, netd, SystemUI, और होम लॉन्चर) को सुरक्षित रखते हैं. साथ ही, स्टैटिक PinnerService mlock फ़ाइल पिनिंग को बदलते हैं.
  • सिस्टम ऐप्लिकेशन के लिए मेमोरी की सीमाएं (<memory-budget> के साथ memory.high): वर्कलोड के हिसाब से तय की गई cgroup v2 memory.high की सॉफ़्ट सीमाएं (आम तौर पर 30 एमबी से 190 एमबी तक) मेनिफ़ेस्ट फ़ाइलों में बताई गई हैं. ये सीमाएं, लगातार और बैकग्राउंड में चलने वाले सिस्टम ऐप्लिकेशन (Bluetooth, Nfc, CalendarProvider, Traceur, MediaProvider, :PhotoPicker, NetworkStack, AppSearch, और TeleService) के लिए हैं. ये सीमाएं, ग्लोबल मेमोरी प्रेशर होने से पहले, कोल्ड फ़ाइल पेज कैश और पोस्ट-बर्स्ट एनॉनिमस हीप को पहले से ही वापस ले लेती हैं.

यहां दी गई टेबल में, दोनों तरीकों की तुलना की गई है:

डाइमेंशन सिस्टम डीमन प्रोटेक्शन फ़्लोर सिस्टम ऐप्लिकेशन के लिए मेमोरी की सीमाएं
प्लैटफ़ॉर्म सर्विस SystemMemoryBudgetManager MemoryLimiter
Cgroup v2 कंट्रोल memory.min हार्ड प्रोटेक्शन फ़्लोर memory.high सॉफ़्ट रिक्लेम सीलिंग
टारगेट प्रोसेस इंटरैक्टिव डीमन और शेल यूज़र इंटरफ़ेस (यूआई) के लिए, इंतज़ार का समय कम होना ज़रूरी है (system_server, surfaceflinger, composer एचएएल, audioserver, cameraserver, netd, SystemUI, और होम ऐप्लिकेशन) लगातार और हेडलेस सिस्टम ऐप्लिकेशन, कॉन्टेंट देने वाले, और बैकग्राउंड में चलने वाली सेवाएं (जैसे कि Bluetooth, Nfc, MediaProvider, NetworkStack, AppSearch, और TeleService)
साइज़िंग मॉडल रन टाइम के दौरान, खुद को ट्यून करने की सुविधा (90% सेटल वर्किंग सेट, जो दुनिया भर में कुल रैम के 10% पर सीमित है). इसमें हर डिवाइस के लिए, ट्यूनिंग टेबल की संख्या शून्य होती है डिक्लेरेटिव <memory-budget> मेनिफ़ेस्ट एक्सएमएल एट्रिब्यूट, रैम टियर, प्रोसेस की स्थिति, और डिसप्ले पिक्सल की संख्या के हिसाब से कैलिब्रेट किए गए हैं
मुख्य फ़ायदा स्टैटिक PinnerService mlock फ़ाइल सूचियों को बदलते समय, मेमोरी प्रेशर की वजह से इंटरैक्टिव यूज़र इंटरफ़ेस (यूआई) में होने वाली रुकावट और वेकअप स्टॉल को रोकता है यह सुविधा, एक बार इस्तेमाल होने वाली फ़ाइल की कैश मेमोरी और बर्स्ट के बाद के हीप को लगातार हटाती है. इससे फ़ोरग्राउंड और कैश मेमोरी में सेव किए गए ऐप्लिकेशन के लिए, ज़्यादा फ़िज़िकल रैम उपलब्ध होती है

<memory-budget> सिंटैक्स, एक्सएमएल एट्रिब्यूट, रनटाइम एपीआई, और ओएस मेमोरी अकाउंटिंग के बारे में ज़्यादा जानने के लिए, ऐप्लिकेशन के लिए मेमोरी बजट सेट करना और Android मेमोरी को समझना और उससे जुड़ी समस्या हल करना लेख पढ़ें.

SystemMemoryBudgetManager की मदद से, ज़रूरी इंटरैक्टिव डीमन को सुरक्षित करना

SystemMemoryBudgetManager, उन प्रोसेस को फ़िज़िकल रैम में बनाए रखता है जो मेमोरी प्रेशर के दौरान, उपयोगकर्ता के इंटरैक्टिव पाथ पर सीधे तौर पर मौजूद होती हैं. mlock की मदद से, APK, ODEX, और VDEX फ़ाइलों की स्टैटिक सूची को मेमोरी में पिन करने के बजाय, SystemMemoryBudgetManager डाइनैमिक cgroup v2 memory.min फ़्लोर को इन प्रोसेस के लिए असाइन करता है. ऐसा PinnerService, config_defaultPinnerServiceFiles और config_pinnerHomePinBytes के लिए करता है:

  • सिस्टम का मुख्य स्लाइस (/sys/fs/cgroup/system): system_server, surfaceflinger, ग्राफ़िक्स composer एचएएल, audioserver, cameraserver, और netd. साथ ही, config_systemMemoryBudgetAdditionalSystemProcesses में लिस्ट किया गया कोई भी नेटिव सिस्टम या वेंडर डीमन, जो किसी खास फ़ॉर्म-फ़ैक्टर के लिए हो. उदाहरण के लिए, एक्सआर डिवाइसों पर android.hardware.perceptionhost और auracamerahal.
  • इंटरैक्टिव ऐप्लिकेशन स्लाइस (/sys/fs/cgroup/apps): कॉन्फ़िगर किए गए SystemUI पैकेज (config_systemUi) की मुख्य प्रोसेस और मौजूदा उपयोगकर्ता के होम रोल होल्डर की मुख्य प्रोसेस.

होम रोल होल्डर के साथ-साथ config_systemUi को सुरक्षित रखने से, सभी डिवाइसों के साइज़, डाइमेंशन या कॉन्फ़िगरेशन पर कवरेज मिलती है. ऐसे डिवाइसों पर जहां config_systemUi एक लाइटवेट कंपैटिबिलिटी पैकेज है (जैसे कि Wear OS, जहां वॉच फ़ेस, सूचनाएं, और टाइल, Home ऐप्लिकेशन के अंदर चलती हैं), उपयोगकर्ता को दिखने वाले शेल को अब भी वर्किंग-सेट की पूरी सुरक्षा मिलती है. अगर होम इंटेंट, config_systemUi पैकेज पर रिज़ॉल्व होता है, तो प्रोसेस को एक बार सुरक्षित किया जाता है. हर पैकेज (processName == packageName) की सिर्फ़ मुख्य प्रोसेस को सुरक्षा की सुविधा मिलती है. इसलिए, सहायक सबप्रोसेस (जैसे कि :screenshot) सुरक्षा बजट का इस्तेमाल नहीं करती हैं.

सेल्फ़-ट्यूनिंग नीति और तीन लेवल का सीजीग्रुप डेलिगेशन

इंटरैक्टिव डीमन का वर्किंग सेट, डिवाइस के साइज़ और रैम की क्षमता के हिसाब से काफ़ी अलग होता है. उदाहरण के लिए, SystemUI स्मार्टवॉच पर करीब 45 एमबी, 6 जीबी रैम वाले फ़ोन पर 60 एमबी, और 16 जीबी रैम वाले फ़ोन पर 180 एमबी मेमोरी का इस्तेमाल करता है. इसलिए, हर डिवाइस के लिए स्टैटिक लुकअप टेबल का इस्तेमाल करना सही नहीं है. इसके बजाय, SystemMemoryBudgetManager हर प्रोसेस के अपने रेज़िडेंट फ़ुटप्रिंट से सीधे तौर पर फ़्लोर की जानकारी लेता है. इसके लिए, वह तीन कॉन्स्टेंट का इस्तेमाल करता है:

नीति पैरामीटर डिफ़ॉल्ट वैल्यू व्यवहार
हर प्रोसेस के लिए फ़्लोर सेटल किए गए memory.current का 90% हिस्सा, जिसमें अनमैप की गई फ़ाइल का कैश मेमोरी शामिल नहीं है यह एनॉनिमस हीप, स्लैब, और हॉट मैप किए गए कोड को सुरक्षित रखता है. साथ ही, सबसे कम इस्तेमाल होने वाले 10% कोड को फिर से इस्तेमाल करने के लिए छोड़ देता है, ताकि वाकई में कम इस्तेमाल होने वाले पेजों को अब भी हटाया जा सके. 90% का अनुपात लागू करने से पहले, अनमैप की गई फ़ाइल कैश (memory.stat में से max(0, file - shmem - file_mapped)) को घटाया जाता है. इससे बूट पैकेज-स्कैन कैश या एक बार पढ़ी गई ऐसेट, फ़्लोर को नहीं बढ़ाती हैं.
ग्लोबल रैम कैप कुल रैम का 10% (config_systemMemoryBudgetLowKb) यह PinnerService config_pinnerMaxPinnedMemoryPercentage गार्डरेल से मेल खाता है. अगर 90% फ़्लोर का योग MemTotal के 10% से ज़्यादा है, तो हर फ़्लोर को एक ही फ़ैक्टर के हिसाब से कम किया जाता है.
रीफ़्रेश करने की फ़्रीक्वेंसी शुरू होने के 1, 4, 16, और 64 सेकंड बाद. इसके बाद, हर 30 मिनट में ज्यामितीय सेटल सीरीज़, हर डीमन के पोस्ट-स्टार्टअप वर्किंग सेट पर कन्वर्ज होती है. इसके लिए, हर डिवाइस के वॉर्म-अप में होने वाली देरी को हार्डकोड नहीं किया जाता. साथ ही, 30 मिनट का स्टेडी-स्टेट रीफ़्रेश, डिवाइस के लाइफ़टाइम के दौरान वर्किंग-सेट की धीरे-धीरे होने वाली बढ़ोतरी और कमी को ट्रैक करता है.

Linux cgroup v2 मेमोरी कंट्रोलर (mm/memcontrol.c:effective_protection) में, लीफ़ cgroup की असरदार memory.min सुरक्षा, उसके पैरंट और ऐंसेस्टर cgroup पर कॉन्फ़िगर किए गए memory.min से सीमित होती है. memory.min को सिर्फ़ लीफ़ pid_<pid> डायरेक्ट्री पर सेट करने से, कोई सुरक्षा नहीं मिलती. ऐसा तब होता है, जब uid_<uid>/memory.min को 0 पर सेट किया गया हो. इसके उलट, सिर्फ़ टॉप /sys/fs/cgroup/system स्लाइस पर फ़्लैट memory.min सेट करने से, दावा न की गई सुरक्षा सभी चाइल्ड यूआईडी और प्रोसेस में बराबर (memory_recursiveprot) बांट दी जाती है. इससे, uid_1000 (android.uid.system) को शेयर करने वाली डाइग्नोस्टिक या सेकंडरी प्रोसेस, surfaceflinger और audioserver से सुरक्षा को हटा सकती हैं.

हर डीमन को अलग-अलग आइसोलेट करने के लिए, SystemMemoryBudgetManager हर बार रीफ़्रेश होने पर, cgroup v2 हैरारकी के तीनों लेवल पर सुरक्षा की सुविधा देता है. इसे इमेज 1 में दिखाया गया है:

  1. लीफ प्रोसेस नोड (pid_<pid>/memory.min): इसे प्रोसेस के (स्केल किए गए) 90% वर्किंग-सेट फ़्लोर पर सेट करें.
  2. यूआईडी नोड (uid_<uid>/memory.min): इसे मैनेज किए जा रहे pid_* लीफ़ फ़्लोर के कुल योग पर सेट किया जाता है. इससे, उस यूआईडी को शेयर करने वाली मैनेज न की जा रही प्रोसेस (जैसे, अन्य AID_SYSTEM डीमन या sharedUserId="android.uid.system" पैकेज) के पास memory.min = 0 होता है और वे सुरक्षा का दावा नहीं कर सकतीं.
  3. टॉप-लेवल स्लाइस (/sys/fs/cgroup/system/memory.min और /sys/fs/cgroup/apps/memory.min): इसे इसके uid_* चाइल्ड स्लाइस के कुल योग पर सेट करें. मैनेज किए जा रहे नोड पर मौजूद memory.low की पुरानी वैल्यू को मिटाकर 0 कर दिया जाता है. साथ ही, पिछली बार रीफ़्रेश किए जाने पर मैनेज की गई uid_* डायरेक्ट्री को रीसेट करके 0 कर दिया जाता है. जैसे, उपयोगकर्ता के लॉन्चर स्विच करने के बाद, पिछला होम लॉन्चर.

/sys/fs/cgroup/system और /sys/fs/cgroup/apps में, तीन लेवल वाली cgroup v2 memory.min सुरक्षा हैरारकी. इसमें दिखाया गया है कि लीफ़ प्रोसेस फ़्लोर को यूआईडी और स्लाइस नोड में जोड़ा गया है. वहीं, असुरक्षित सिबलिंग प्रोसेस शून्य पर बनी हुई हैं

पहली इमेज. cgroup स्लाइस में, तीन लेवल की सुरक्षा के लिए फ़्लोर डेलिगेशन.

एमजीएलआरयू कर्नेल पर, memory.low के बजाय memory.min का इस्तेमाल क्यों किया जाता है

मल्टी-जनरेशनल एलआरयू (एमजीएलआरयू, Android 6.6 और इसके बाद के वर्शन वाले कर्नेल पर डिफ़ॉल्ट पेज रिक्लेम इंजन) के तहत, memory.low और memory.min, mm/vmscan.c:shrink_one में अलग-अलग तरह से काम करते हैं:

  • memory.low (सिंगल-पास डिफ़रल): ऐसा cgroup जिसका इस्तेमाल memory.low से कम है, उसे memcg LRU (MEMCG_LRU_TAIL) की टेल पर घुमाया जाता है और एक रिक्लेम पास के लिए छोड़ दिया जाता है. मेमोरी पर लगातार दबाव पड़ने पर, अगले पास में कर्नेल, cgroup को पूरी तरह से वापस ले लेता है और MEMCG_LOW इवेंट को लॉग करता है. फ़िज़िकल डिवाइस के स्ट्रेस टेस्ट में, मेमोरी का इस्तेमाल करने वाले किसी अज्ञात ऐप्लिकेशन ने फ़िज़िकल रैम का 85% हिस्सा इस्तेमाल किया. इस दौरान, memory.low ने स्मार्टवॉच पर SystemUI को ~45 एमबी से ~5 से 10 एमबी तक कम करने की अनुमति दी. साथ ही, 16 जीबी वाले फ़ोल्ड किए जा सकने वाले डिवाइस पर, audioserver को ~14 एमबी के फ़्लोर के मुकाबले 15 एमबी से 2 एमबी तक कम करने की अनुमति दी.
  • memory.min (हार्ड प्रोटेक्शन फ़्लोर): ऐसा cgroup जिसका इस्तेमाल memory.min से कम है, उसे ग्लोबल रिक्लेम से स्किप कर दिया जाता है. जैसा कि इमेज 2 में दिखाया गया है, 85% रैम प्रेशर वाले वर्कलोड के तहत, memory.min हर डीमन के सबसे कम इस्तेमाल होने वाले 10% हिस्से को हटा देता है. साथ ही, SystemUI, surfaceflinger, और audioserver को उनके 90% फ़्लोर पर रखता है. इसमें, बिना नाम वाले पेज के लिए कोई रीफ़ॉल्ट नहीं होता और lmkd प्रोसेस को बंद करने की संख्या में कोई बढ़ोतरी नहीं होती.

memory.min, PinnerService mlock की सुरक्षा से मैच करने वाला हार्ड फ़िज़िकल रिज़र्वेशन उपलब्ध कराता है. यह रिज़र्वेशन, पिन किए गए की जगह लेता है. इसका फ़ायदा यह है कि कर्नल एलआरयू (स्टैटिक फ़ाइल लिस्ट के बजाय) यह चुनता है कि कौनसे हॉट कोड, हीप, और ग्राफ़िक पेज मेमोरी में बने रहें.

सिस्टम मेमोरी पर दबाव बढ़ने पर, डीमन की रेज़िडेंट मेमोरी की तुलना. यह दबाव, फ़िज़िकल रैम के 40% से 95% तक बढ़ने पर होता है. इसमें दिखाया गया है कि memory.min, 90% के फ़्लोर पर बना रहता है, जबकि memory.low, एक MGLRU डिफ़रल पास के बाद बंद हो जाता है

दूसरी इमेज. मेमोरी का दबाव बढ़ने पर, डीमन वर्किंग-सेट को बनाए रखना.

बूट प्रीवॉर्मिंग और पोस्ट-बूट अनमैप्ड फ़ाइल कैश मेमोरी को फिर से हासिल करना

config_defaultPinnerServiceFiles के PinnerService स्थायी mlock को बदलने के लिए, बूट-टाइम के दो व्यवहारों के बीच तालमेल बनाना ज़रूरी है:

  • बैकग्राउंड में बूट होने के दौरान, एक बार में प्रीवॉर्मिंग (PinnerService): पेज कैश मेमोरी को वॉर्म किए बिना PinnerService mlock को स्किप करने से, SystemUI के शुरू होने (OnBootPhase_550) पर, सिंक्रोनस बूट पाथ में SystemUI APK, ODEX, और VDEX डिस्क की करीब 95 एमबी मेमोरी को रीड किया जाता है. इससे बूट होने में 300 से 450 मिलीसेकंड ज़्यादा लगते हैं. madvise(MADV_WILLNEED) को कॉल करना काफ़ी नहीं है, क्योंकि कर्नेल में force_page_cache_ra, एक MADV_WILLNEED कॉल को एक रीडअहेड विंडो (आम तौर पर 128 केबी से 2 एमबी) तक सीमित कर देता है. इसके बजाय, जब SystemMemoryBudgetManager.shouldSkipDefaultSystemPinning चालू होता है, तब PinnerService, onStart के दौरान बैकग्राउंड थ्रेड पर config_defaultPinnerServiceFiles में मौजूद हर फ़ाइल को मैप करता है. साथ ही, Os.mlock की मदद से फ़ाइल को पेज कैश में सिंक्रोनस तरीके से पॉप्युलेट करता है. इसके बाद, Os.munlock, munmap, और close को तुरंत कॉल करता है. इससे तेज़ी से चालू होने की सुविधा बनी रहती है. साथ ही, हमेशा के लिए लॉक किए गए बाइट की संख्या शून्य रहती है, ताकि कोल्ड स्टार्टअप पेजों को बाद में हटाया जा सके.
  • system_server बूट पैकेज-स्कैन कैश मेमोरी को हटाना और फिर से हासिल करना: बूट के दौरान, PackageManagerService इंस्टॉल किए गए APKs को स्कैन करता है. इससे 1.5 से 2.5 जीबी की कोल्ड अनमैप की गई फ़ाइल पेज कैश मेमोरी, system_server cgroup पर चार्ज होती है. अगर system_server के memory.min कैलकुलेशन में शामिल किया जाता है, तो कोल्ड बूट कैश मेमोरी, ग्लोबल रैम की 10% सीमा को पूरा करती है. साथ ही, surfaceflinger, SystemUI, और audioserver के सुरक्षा फ़्लोर को आनुपातिक रूप से कम करती है. कैप स्टार्वेशन को रोकने के लिए, SystemMemoryBudgetManager system_server के लिए memory.stat को पढ़ता है. साथ ही, अनमैप की गई फ़ाइल कैश को max(0, file - shmem - file_mapped) के तौर पर कंप्यूट करता है. इसके बाद, 90% फ़्लोर को कंप्यूट करने से पहले, अनमैप की गई उस कैश मेमोरी को memory.current से घटाता है, जैसा कि इमेज 3 में दिखाया गया है. इसके अलावा, PHASE_BOOT_COMPLETED के 10 सेकंड बाद, मैनेजर Linux 6.2 और इसके बाद के वर्शन वाले कर्नेल पर, system_server के memory.reclaim नोड में <unmapped_file> swappiness=0 को एक बार में लिखता है. इससे, बिना नाम वाले हीप को स्वैप किए बिना या मैप किए गए DEX और कोड पेजों को हटाए बिना, कोल्ड पैकेज-स्कैन कैश को हटाया जा सकता है. android14-6.1 जैसे पुराने कर्नेल पर, EINVAL के साथ swappiness=0 को अस्वीकार कर दिया जाता है. ऐसे में, मैनेजर memory.reclaim राइट को स्किप कर देता है. इसके बजाय, वह बिना अनुमति वाले रिक्लेम राइट पर वापस नहीं जाता, जो बूट के दौरान अज्ञात हीप को स्वैप कर देता है.

बूट होने के बाद, system_server मेमोरी की टाइमलाइन. इसमें 1, 4, 16, और 64 सेकंड पर ज्यामितीय सेटल रीफ़्रेश, 90% memory.min फ़्लोर से अनमैप किए गए बूट पैकेज-स्कैन कैश मेमोरी को बाहर रखना, और swappiness=0 के साथ वन-शॉट memory.reclaim को दिखाया गया है

तीसरी इमेज. बूट होने के बाद, सुरक्षा के लिए तय की गई कम से कम मेमोरी को कन्वर्ज करना और कैश मेमोरी को फिर से हासिल करना.

कर्नेल से जुड़ी ज़रूरी शर्तें और डिवाइस कॉन्फ़िगरेशन ओवरले

SystemMemoryBudgetManager, Linux कर्नेल में mem_cgroup_calculate_protection पर निर्भर करता है, ताकि नेस्ट किए गए cgroups में memory.min को लागू किया जा सके. Android के कॉमन कर्नेल android15-6.6 और android16-6.12 में, एमजीएलआरयू नेस्टेड प्रोटेक्शन की सुविधा पहले से मौजूद होती है. एमजीएलआरयू की सुविधा वाले android14-6.1 पर चलने वाले डिवाइसों के लिए, mm/vmscan.c और mm/memcontrol.c में तीन अपस्ट्रीम बैकपोर्ट की ज़रूरत होती है. इससे खाली इंटरमीडिएट स्लाइस और यूआईडी सीजीग्रुप, लीफ़ प्रोसेस सीजीग्रुप को सुरक्षा देते हैं:

  • 30d77b7eef01 (mm/mglru: fix ineffective protection calculation)
  • 8aa420617918 (mm/mglru: respect min_ttl_ms with memcgs)
  • adb8213014b2 (mm: memcg: fix stale protection of reclaim target memcg)

ओईएम, नीचे दिए गए फ़्रेमवर्क रिसॉर्स ओवरले का इस्तेमाल करके, SystemMemoryBudgetManager को पसंद के मुताबिक बना सकते हैं या बंद कर सकते हैं:

  • config_systemMemoryBudgetLowKb (इंटीजर, केबी): 0 (डिफ़ॉल्ट) सभी memory.min फ़्लोर के योग को कुल फ़िज़िकल रैम के 10% पर सीमित करता है. पॉज़िटिव वैल्यू, केबी में एक तय सीमा सेट करती है. वहीं, नेगेटिव वैल्यू, डिवाइस पर SystemMemoryBudgetManager को बंद कर देती है. इससे लेगसी PinnerService स्टैटिक mlock पिनिंग बनी रहती है.
  • config_systemMemoryBudgetHighKb (integer, KB): 0 (default) leaves /sys/fs/cgroup/system/memory.high at max. पॉज़िटिव वैल्यू, टॉप स्लाइस memory.high की सीलिंग सेट करती है. यह वैल्यू, फ़्लोर कैप से ज़्यादा या उसके बराबर होनी चाहिए.
  • config_systemMemoryBudgetAdditionalSystemProcesses (string-array): /sys/fs/cgroup/system में, ज़्यादा इंतज़ार के समय वाले नेटिव सिस्टम या वेंडर डीमन प्रोसेस के अतिरिक्त नामों की सूची (10000 के तहत सिस्टम और वेंडर यूआईडी के लिए, /proc/<pid>/cmdline में /proc/<pid>/comm या argv[0] के बेसनेम से मैच किया गया) ताकि डिफ़ॉल्ट छह सिस्टम डीमन के साथ-साथ 90% memory.min फ़्लोर से सुरक्षित रखा जा सके. init.rc में memory.min सेट करने के बजाय, इस ओवरले का इस्तेमाल करें. ऐसा इसलिए, क्योंकि SystemMemoryBudgetManager, uid_<uid>/memory.min को मैनेज की गई लीफ़ प्रोसेस के योग के तौर पर फिर से कैलकुलेट करता है. साथ ही, हर बार रीफ़्रेश करने पर, मैनेज नहीं किए गए uid_* नोड को हटा देता है. उदाहरण के लिए, कोई XR डिवाइस अपने स्पेशल ट्रैकिंग और कैमरा एचएएल डीमन को इस ओवरले की मदद से सुरक्षित रख सकता है:

    <string-array translatable="false" name="config_systemMemoryBudgetAdditionalSystemProcesses">
        <item>android.hardware.perceptionhost</item>
        <item>auracamerahal</item>
    </string-array>
    
  • config_systemUi (स्ट्रिंग): प्लैटफ़ॉर्म के SystemUI ऐप्लिकेशन का पैकेज नेम, जिसे MATCH_SYSTEM_ONLY की मदद से रिज़ॉल्व किया गया है.

किसी डिवाइस पर चालू SystemMemoryBudgetManager फ़्लोर और cgroup नोड की जांच करने के लिए, यह कमांड चलाएं:

# Inspect policy state, resolved PIDs, and applied floors in dumpsys meminfo
adb shell dumpsys meminfo | sed -n '/System Memory Budgets/,$p'
# Inspect raw cgroup v2 memory.min nodes across the system and apps slices
adb shell head -n 1 \
  /sys/fs/cgroup/system/memory.min \
  /sys/fs/cgroup/system/uid_*/memory.min \
  /sys/fs/cgroup/system/uid_*/pid_*/memory.min \
  /sys/fs/cgroup/apps/memory.min \
  /sys/fs/cgroup/apps/uid_*/memory.min \
  /sys/fs/cgroup/apps/uid_*/pid_*/memory.min

बैकग्राउंड में लगातार चलने वाले सिस्टम ऐप्लिकेशन के लिए, मेमोरी बजट तय करना

SystemMemoryBudgetManager, memory.min फ़्लोर का इस्तेमाल करके, इंटरैक्टिव डीमन को रीक्लेम होने से बचाता है. हालांकि, कई अन्य सिस्टम ऐप्लिकेशन और खास अधिकार वाली प्लैटफ़ॉर्म सेवाएं, बैकग्राउंड में लंबे समय तक PERSISTENT प्रोसेस, बाउंड फ़ोरग्राउंड सेवाओं या अक्सर क्वेरी किए जाने वाले कॉन्टेंट देने वाले के तौर पर चलती हैं. मेमोरी की साफ़ तौर पर तय की गई सीमा के बिना, इन प्रोसेस पर तब तक कोई पाबंदी नहीं लगाई जाती, जब तक कि वे कई गीगाबाइट की मेमोरी लिमिटर की सीमाओं तक नहीं पहुंच जातीं. जैसे, स्क्रीन पर दिखने वाले ऐप्लिकेशन के लिए 4 जीबी से 10 जीबी और बैकग्राउंड में चलने वाले ऐप्लिकेशन के लिए 2 जीबी से 5 जीबी. इसके अलावा, ऐसा तब भी होता है, जब ग्लोबल मेमोरी प्रेशर की वजह से, पूरे सिस्टम में kswapd और कम मेमोरी वाले किलर (lmkd) की गतिविधि ट्रिगर होती है.

अगर बैकग्राउंड में चल रही सिस्टम प्रोसेस पर कोई पाबंदी न लगाई जाए, तो अक्सर उनमें बड़ी मात्रा में कोल्ड मेमोरी जमा हो जाती है. यह मेमोरी, उनके ऐक्टिव वर्किंग सेट का हिस्सा नहीं होती:

  • अनबाउंडेड फ़ाइल पेज कैश: मीडिया को इंडेक्स करने वाली, डेटाबेस खोजने वाली या कैलेंडर और प्रोवाइडर टेबल को सिंक करने वाली सेवाएं (जैसे, AppSearch, MediaProvider, और CalendarProvider), डिस्क पर मौजूद बड़ी फ़ाइलों को मैप या स्ट्रीम करती हैं. cgroup memory.high की सीमा के बिना, एक बार पढ़े गए क्लीन फ़ाइल-बैक्ड पेज, प्रोसेस cgroup से हमेशा के लिए चार्ज रहते हैं. इससे सैकड़ों मेगाबाइट की फ़िज़िकल रैम खर्च होती है.
  • बर्स्ट के बाद, पहचान छिपाकर हीप को बनाए रखना: ऐसी सेवाएं जिनमें कुछ समय के लिए गतिविधि में बढ़ोतरी होती है, जैसे कि Bluetooth ऑडियो और BLE डिस्कवरी बर्स्ट, NetworkStack कैप्टिव-पोर्टल टीएलएस की पुष्टि या यूज़र इंटरफ़ेस (यूआई) हेल्पर का बैकग्राउंड में ट्रांज़िशन, बर्स्ट पूरा होने के बाद भी, पहचान छिपाकर हीप पेजों को बनाए रख सकती हैं.

सिस्टम ऐप्लिकेशन पर, कैलिब्रेट किए गए <memory-budget> एलान (memory.high सॉफ़्ट लिमिट, आम तौर पर 30 एमबी से 190 एमबी) लागू करने से दो मुख्य फ़ायदे मिलते हैं:

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

सिस्टम ऐप्लिकेशन के लिए मेमोरी बजट कॉन्फ़िगर करना

सिस्टम ऐप्लिकेशन, सामान्य ऐप्लिकेशन की तरह ही <memory-budget> मेनिफ़ेस्ट सिंटैक्स, प्रोसेस-स्टेट कैस्केडिंग, डिसप्ले-डेंसिटी स्केलिंग, और लास्ट-मैचिंग-क्लॉज़ रिज़ॉल्यूशन रूल का इस्तेमाल करते हैं. एट्रिब्यूट की पूरी सूची देखने के लिए, एक्सएमएल एट्रिब्यूट का रेफ़रंस देखें.

ओईएम, सिस्टम ऐप्लिकेशन के AndroidManifest.xml में सीधे तौर पर <memory-budget> एलिमेंट का एलान कर सकते हैं. इसके अलावा, वे Android.bp में additional_manifests प्रॉपर्टी का इस्तेमाल करके, उन्हें किसी अलग एक्सएमएल फ़ाइल से मर्ज कर सकते हैं. आम तौर पर, AOSP रेफ़रंस सिस्टम ऐप्लिकेशन के बजट को अलग-अलग *-budget.xml फ़ाइलों में सेव करता है. ये फ़ाइलें, कोर प्लैटफ़ॉर्म पैकेज के लिए frameworks/base/core/memory_budgets/ में या packages/modules/NetworkStack/memory_budgets/, packages/modules/AppSearch/apk/, और packages/services/Telephony/ जैसे अलग-अलग मॉड्यूल के साथ मौजूद होती हैं. साथ ही, इन्हें additional_manifests के साथ अटैच करता है:

android_app {
    name: "TeleService",
    manifest: "AndroidManifest.xml",
    additional_manifests: ["Telephony-budget.xml"],
    // ...
}

पैकेज-लेवल (यूआईडी) बनाम प्रोसेस-लेवल (पीआईडी) स्कोपिंग

पैकेज-लेवल का बेसलाइन बजट (<application>) या मल्टी-प्रोसेस बजट (<processes><process>) तय करने से यह तय होता है कि कर्नेल, यूआईडी सीजीग्रुप लेवल (/sys/fs/cgroup/uid_<uid>/memory.high) पर या हर पीआईडी सीजीग्रुप लेवल (/sys/fs/cgroup/uid_<uid>/pid_<pid>/memory.high) पर memory.high को लागू करता है या नहीं. जैसा कि इमेज 4 में दिखाया गया है:

  • पैकेज-लेवल (<application>) बजट: इसका इस्तेमाल तब करें, जब APK अपने यूआईडी के तहत एक ही प्रोसेस चलाता हो. उदाहरण के लिए, Bluetooth, Nfc, NetworkStack, CalendarProvider, Traceur या AppSearch.
  • प्रोसेस-लेवल (<processes><process>) बजट: इसका इस्तेमाल तब करें, जब APK, अन्य सिस्टम पैकेज के साथ android:sharedUserId (जैसे, यूआईडी 1001 वाला android.uid.phone या यूआईडी 1000 वाला android.uid.system) शेयर करता हो, ताकि उस यूआईडी को शेयर करने वाली अन्य प्रोसेस को एक साथ थ्रॉटल न किया जाए. इसके अलावा, इसका इस्तेमाल तब करें, जब कोई APK, अलग-अलग मेमोरी रोल वाली कई प्रोसेस चलाता हो. उदाहरण के लिए, बैकग्राउंड MediaProvider डीमन और :PhotoPicker यूआई सबप्रोसेस.

Cgroup v2 की हैरारकी, जिसमें यूआईडी-लेवल पर memory.high को लागू करने की तुलना, शेयर किए गए सिस्टम यूआईडी और मल्टी-प्रोसेस APK के लिए पीआईडी के हिसाब से memory.high को लागू करने से की गई है

चौथी इमेज. यूआईडी-लेवल और पीआईडी-लेवल की मेमोरी सीलिंग.

यहां दिए गए उदाहरण में MediaProvider-budget.xml को दिखाया गया है. यह रैम के हिसाब से तय किए गए बजट को डिफ़ॉल्ट MediaProvider प्रोसेस (<process>) और डिसप्ले के हिसाब से तय किए गए बजट को :PhotoPicker यूज़र इंटरफ़ेस (यूआई) प्रोसेस के दायरे में लाता है. हर android:maxDeviceRamMb एट्रिब्यूट, उन डिवाइसों से मैच करता है जिनकी कुल फ़िज़िकल रैम (MemTotal), एमबी में तय किए गए थ्रेशोल्ड के बराबर या उससे कम होती है. अगर यह एट्रिब्यूट शामिल नहीं किया जाता है, तो यह किसी भी रैम क्षमता वाले डिवाइसों पर लागू होता है. क्लॉज़ को बिना किसी पाबंदी वाले डिफ़ॉल्ट से लेकर सबसे छोटे maxDeviceRamMb थ्रेशोल्ड के क्रम में लगाया जाता है, ताकि आखिरी मैचिंग क्लॉज़ रिज़ॉल्यूशन नियम, लागू होने वाले सबसे सख्त टियर को चुन सके:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.providers.media.module">
    <uses-sdk android:targetSdkVersion="10000" />
    <application>
        <processes>
            <process>
                <memory-budget
                    android:maxMb="85" />
                <memory-budget
                    android:maxMb="75"
                    android:maxDeviceRamMb="12288" />
                <memory-budget
                    android:maxMb="60"
                    android:maxDeviceRamMb="8192" />
            </process>
            <process android:process=":PhotoPicker">
                <memory-budget
                    android:maxMb="85"
                    android:additionalBytesPerDisplayPixel="25" />
            </process>
        </processes>
    </application>
</manifest>

Google Pixel डिवाइसों पर, एओएसपी के रेफ़रंस बजट को कैसे कैलिब्रेट किया गया

AOSP में सिस्टम ऐप्लिकेशन के लिए तय की गई मेमोरी की सीमा को, Google Pixel डिवाइसों के हिसाब से कैलिब्रेट किया गया था. इनमें 6 जीबी से लेकर 16 जीबी तक की फ़िज़िकल रैम वाले डिवाइस शामिल थे. जैसे, 6 जीबी, 8 जीबी, 12 जीबी, और 16 जीबी रैम वाले फ़ोन, फ़ोल्ड किए जा सकने वाले डिवाइस, और टैबलेट. साथ ही, इनमें ~1.2 एमपी (720×1600) से लेकर ~4.5 एमपी (2076×2152) तक के डिसप्ले रिज़ॉल्यूशन वाले डिवाइस शामिल थे.

कैलिब्रेशन में, एक-दूसरे के पूरक दो डेटा सोर्स को जोड़ा गया: फ़्लीट-वाइड cgroup v2 फ़ील्ड टेलीमेट्री और कंट्रोल किए गए फ़िज़िकल-डिवाइस memory.high की मदद से, क्रिटिकल यूज़र जर्नी (सीयूजे) के दौरान की गई जांच.

सभी फ़्लीट के लिए cgroup v2 टेलीमेट्री का विश्लेषण

सभी डिवाइसों से समय-समय पर cgroup v2 के स्नैपशॉट इकट्ठा किए गए. इसके बाद, उन्हें फ़िज़िकल रैम टियर (≤8 जीबी, 12 जीबी, और ≥16 जीबी) और प्रोसेस स्टेट बकेट (foreground, perceptible, background, और cached) के हिसाब से बांटा गया. आंकड़ों के विश्लेषण के लिए, छह सिद्धांतों का पालन किया गया:

  • memory.current को वर्किंग सेट और फिर से इस्तेमाल की जा सकने वाली फ़ाइल कैश मेमोरी में बांटना: memory.high, cgroup के रेज़िडेंट चार्ज (memory.current) को कंट्रोल करता है. इसलिए, हर स्नैपशॉट को memory.stat का इस्तेमाल करके, एनॉनिमस हीप (anon), स्वैप किए गए एनॉनिमस पेज (swap), और फ़ाइल-बैक्ड पेज कैश मेमोरी (file) में बांटा गया. इसे इमेज 5 में दिखाया गया है. बिना सोर्स फ़ाइल वाली ऐसी मेमोरी जिसे कंप्रेस नहीं किया जा सकता, उसकी कुल मांग (P99 पर anon + swap) से, बजट के लिए कम से कम वर्किंग-सेट फ़्लोर तय होता है. इससे प्रोसेस के दौरान, ZRAM के खत्म होने का खतरा नहीं रहता. वहीं, memory.current P99 और anon + swap P99 के बीच का बड़ा अंतर, फ़ाइल पेज कैश को दिखाता है. memory.high इसे फिर से हासिल कर सकता है. हालांकि, इसके लिए ज़रूरी है कि कैश मेमोरी अनमैप हो और उसका इस्तेमाल न किया जा रहा हो.
  • ऐसी मेमोरी का हिसाब लगाएं जिसे memory.high फिर से इस्तेमाल नहीं कर सकता: सभी फ़ाइल और शेयर की गई मेमोरी, कोल्ड कैश नहीं होती. लॉक किए गए पेज (memory.stat में unevictable, जैसे कि mlock के साथ लॉक की गई शेयर की गई मेमोरी या किसी वर्चुअल मशीन की लॉक की गई गेस्ट रैम, जिसके होस्ट प्रोसेस को ऐप्लिकेशन के cgroup से चार्ज किया जाता है) बजट के तहत एक हार्ड फ़्लोर सेट करते हैं. साथ ही, मैप किए गए पेज (file_mapped) अक्सर ऐक्टिव वर्किंग सेट का हिस्सा होते हैं. फ़ाइल मेमोरी को फिर से इस्तेमाल करने लायक मानने से पहले, unevictable, shmem, और file_mapped को देखें. उदाहरण के लिए, AppSearch की करीब 100 एमबी मेमोरी लॉक होती है और उसे खाली नहीं किया जा सकता. इसलिए, इसके बजट में वह फ़्लोर और उसका हॉट होस्ट वर्किंग सेट शामिल होता है. साथ ही, लॉक किए गए हिस्से को बढ़ाने के लिए हेडरूम भी होता है.
  • आइडल-स्टेट डाइल्यूशन को फ़िल्टर करना: कॉन्टेंट देने वाले (CalendarProvider) और मांग पर उपलब्ध डेवलपर टूल (Traceur), फ़ील्ड स्नैपशॉट का 70% से 99.9% हिस्सा, करीब 1 एमबी से 3 एमबी तक इस्तेमाल नहीं करते. सभी स्नैपशॉट में रॉ पर्सेंटाइल की गिनती करने से, P95 और P99 के अनुमानों पर काफ़ी असर पड़ता है. इसके बजाय, पर्सेंटाइल और कॉन्फ़िडेंस इंटरवल का हिसाब, बिना किसी बदलाव के ऐक्टिव स्नैपशॉट के आधार पर लगाया गया. इसमें CACHED स्नैपशॉट और आइडल बेसलाइन स्नैपशॉट को फ़िल्टर करके हटा दिया गया.
  • डिवाइस के हिसाब से क्लस्टर किए गए कॉन्फ़िडेंस इंटरवल का इस्तेमाल करें: एक ही डिवाइस समय के साथ कई समय-समय पर स्नैपशॉट देता है. इसलिए, स्नैपशॉट को अलग-अलग सैंपल के तौर पर मानने से, टेल अनसर्टेनिटी कम हो जाती है. P95 और P99 के सभी वर्किंग सेट के अनुमानों का आकलन, डिवाइस के क्लस्टर के हिसाब से 95% कॉन्फ़िडेंस इंटरवल का इस्तेमाल करके किया गया. इसके लिए, अलग-अलग डिवाइस की संख्या N_dev को सैंपल साइज़ के तौर पर इस्तेमाल किया गया.
  • memory.peak की मदद से, कुछ समय के लिए होने वाले बर्स्ट को कैप्चर करें: समय-समय पर सैंपल लेने से, कम समय के लिए होने वाले और ज़्यादा मेमोरी का इस्तेमाल करने वाले बर्स्ट छूट सकते हैं. जैसे, NetworkStack कैप्टिव-पोर्टल टीएलएस की पुष्टि और एक साथ होने वाली डीएनएस जांच या Bluetooth A2DP और बीएलई को शुरू करना. बर्स्टी सेवाओं के लिए, बजट इस तरह से तय किए गए थे कि वे रैम के अलग-अलग टियर में, लाइफ़टाइम कर्नेल memory.peak के हाई-वॉटर मार्क के 90वें से 95वें पर्सेंटाइल को कवर कर सकें.
  • पुष्टि करें कि रैम टियर, रैम को दिखाता है, न कि डिवाइस की जनरेशन को: नए डिवाइसों में ज़्यादा रैम और ज़्यादा सुविधाएं होती हैं. इसलिए, रैम टियर के साथ बढ़ने वाला वर्किंग सेट, असल में सुविधाओं के साथ बढ़ सकता है. maxDeviceRamMb टियर जोड़ने से पहले, डेटा को डिवाइस मॉडल के हिसाब से स्लाइस करें. उदाहरण के लिए, एक ही जनरेशन के 12 जीबी और 16 जीबी वाले फ़ोन पर, com.android.phone उसी P99 anon + swap (करीब 85 से 90 एमबी) तक पहुंचता है. वहीं, फ़ोल्ड होने वाले पुराने 12 जीबी फ़ोन पर, यह 40 एमबी का इस्तेमाल करता है. इसलिए, इसके बजट में रैम टियर के बिना एक ही वैल्यू का इस्तेमाल किया जाता है. इससे, कम रैम वाले डिवाइस में नई टेलीफ़ोनी सुविधाएं उपलब्ध कराने के लिए, बजट कम नहीं पड़ता.

समय के साथ, cgroup मेमोरी को कंप्रेस न किए जा सकने वाले एनॉनिमस वर्किंग सेट, ऐक्टिव फ़ाइल कैश विंडो, और कोल्ड वन-टाइम फ़ाइल कैश में बांट दिया जाता है. इसे memory.high से ज़्यादा मेमोरी होने पर वापस हासिल किया जाता है

पांचवीं इमेज. वर्किंग सेट बनाम फिर से इस्तेमाल की जा सकने वाली फ़ाइल कैश मेमोरी.

फ़िज़िकल-डिवाइस सीयूजे और डिसप्ले-स्केलिंग स्वीप

टेलीमेट्री से मिले बजट की पुष्टि करने और डिसप्ले के हिसाब से बजट (जैसे, :PhotoPicker) और स्थिति के हिसाब से बैकग्राउंड बजट (जैसे, Traceur) को कैलिब्रेट करने के लिए, 6 जीबी से 16 जीबी रैम वाले फ़िज़िकल डिवाइसों पर लाइव memory.high स्वीप किए गए:

  • ज़ीरो-रीफ़ॉल्ट नी का पता लगाना: एंड-टू-एंड सीयूजे (जैसे, फ़ोटो ग्रिड को तेज़ी से फ़्लिंग करना, एल्बम ब्राउज़ करना, फ़ुल-स्क्रीन हाई-रिज़ॉल्यूशन वाली फ़ोटो और वीडियो की झलक को स्वाइप करना, और ट्रेस रिकॉर्ड करना और सेव करना) को चलाते समय, cgroup memory.stat काउंटर (workingset_refault_anon, workingset_refault_file, pgscan, और pgsteal) और memory.events (high) को मेज़र करते समय, memory.high को कम किया गया. जैसा कि इमेज 6 में दिखाया गया है, ऑप्टिमाइज़ किया गया बजट, ज़ीरो-रीफ़ॉल्ट नी से थोड़ा ज़्यादा है. ज़ीरो-रीफ़ॉल्ट नी वह थ्रेशोल्ड है जहां कर्नेल, कोल्ड वन-टाइम फ़ाइल कैश (pgsteal > 0) को फिर से हासिल करता है. इसके लिए, उसे ऐक्टिव वर्किंग-सेट थ्रैशिंग (workingset_refault == 0) को ट्रिगर करने की ज़रूरत नहीं पड़ती.
  • अलग-अलग रिज़ॉल्यूशन पर डिसप्ले-पिक्सल स्केलिंग की पुष्टि करना: हमने :PhotoPicker को चार फ़िज़िकल और एम्युलेट किए गए डिसप्ले रिज़ॉल्यूशन पर टेस्ट किया. ये रिज़ॉल्यूशन, ~1.2 एमपी (720×1600) से लेकर ~4.5 एमपी (2076×2152 अनफ़ोल्ड किया गया इनर डिसप्ले) तक थे. इससे यह पुष्टि हुई कि इसका ऐक्टिव Glide और ViewPager2 बिटमैप वर्किंग सेट, डिसप्ले पिक्सल की संख्या के हिसाब से लीनियर तरीके से स्केल होता है. बिना डिसप्ले वाली बेसलाइन भी डिवाइस के हिसाब से अलग-अलग होती है: करीब-करीब एक जैसी पिक्सल संख्या पर, 16 जीबी वाले फ़ोल्ड किए जा सकने वाले डिवाइस के बाहरी डिसप्ले पर ज़ीरो-रीफ़ॉल्ट नी करीब 140 एमबी था. वहीं, 6 जीबी वाले फ़ोन पर यह 110 से 125 एमबी था. टेस्ट किए गए हर डिसप्ले पर, 85 एमबी की बेसलाइन और हर डिसप्ले पिक्सल के लिए 25 बाइट (android:maxMb="85" और android:additionalBytesPerDisplayPixel="25"), मेज़र किए गए नी से ज़्यादा रहती है. एचडी+ पर ~110 एमबी, एफ़एचडी+ पर ~145 एमबी, और अनफ़ोल्ड किए गए डिसप्ले पर ~190 एमबी.
  • बैकग्राउंड में ART गार्बेज कलेक्शन की पुष्टि करना: cgroup v2 में, Zygote-शेयर किए गए boot.art और फ़्रेमवर्क इमेज पेज, Zygote के cgroup से चार्ज होते रहते हैं. साथ ही, ART के साथ-साथ मार्क-कॉम्पैक्ट (सीएमसी) गार्बेज कलेक्शन के दौरान, इन्हें इम्यून स्पेस के तौर पर माना जाता है. स्ट्रेस टेस्टिंग से यह पुष्टि हुई कि जब कोई सिस्टम यूज़र इंटरफ़ेस (यूआई) ऐप्लिकेशन background पर ट्रांज़िशन करता है, तो memory.high को टाइट करने से (उदाहरण के लिए, Traceur पर 30 एमबी तक) निजी हीप और पेज कैश को बिना किसी जीसी थ्रैशिंग या स्वैप गतिविधि के साफ़ तौर पर बाउंड किया जाता है.

सीयूजे के दौरान, memory.high को कम करने पर, कोल्ड कैश मेमोरी को फिर से हासिल करने (pgsteal) और वर्किंग-सेट थ्रैशिंग (workingset_refault) का प्लॉट. इसमें ज़ीरो-रीफ़ॉल्ट नी और टारगेट बजट को हाइलाइट किया गया है

छठी इमेज. सीयूजे के दौरान, ज़ीरो-रीफ़ॉल्ट नी का पता लगाना.

AOSP के लिए रेफ़रंस सिस्टम ऐप्लिकेशन के बजट की खास जानकारी

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

सिस्टम पैकेज या प्रोसेस दायरा ≤8 जीबी रैम 12 जीबी रैम ≥16 जीबी रैम प्राइमरी कैलिब्रेशन ड्राइवर
com.android.bluetooth यूआईडी (<application>) 80 एमबी 95 एमबी 110 एमबी इसमें P90 लाइफ़टाइम memory.peak बर्स्ट (~75 से 110 एमबी) और >2x P99 ऐक्टिव memory.current शामिल हैं.
com.android.nfc यूआईडी (<application>) 50 एमबी 60 एमबी 60 एमबी यह कोल्ड फ़ाइल कैश मेमोरी को ट्रिम करते समय, चालू होस्ट कार्ड एम्युलेशन P99 (ज़्यादा से ज़्यादा ~50 एमबी) और P90 से P95 memory.peak को कवर करता है.
com.android.providers.calendar यूआईडी (<application>) डिफ़ॉल्ट रूप से 70 एमबी (फ़ोरग्राउंड)
50 एमबी perceptible और background
ऐक्टिव वर्किंग सेट, रैम के अलग-अलग टियर के हिसाब से नहीं बदलता. बैकग्राउंड बजट, रिमाइंडर अलार्म के लिए हेडस्पेस को बनाए रखता है. साथ ही, सिंक करने के दौरान SQLite पेज कैश को ट्रिम करता है.
com.android.traceur यूआईडी (<application>) डिफ़ॉल्ट तौर पर 85 एमबी (फ़ोरग्राउंड और परसेप्टिबल)
30 एमबी background
ट्रेस रिकॉर्डिंग और एक्सपोर्ट (TraceService) के दौरान, 85 एमबी का पूरा हेडरूम सुरक्षित रखता है. साथ ही, इस्तेमाल न होने और ब्रॉडकास्ट की स्थिति को 30 एमबी पर सीमित करता है.
com.android.providers.media.module (मुख्य) पीआईडी (<process>) 60 एमबी 75 एमबी 85 एमबी कोल्ड SQLite और थंबनेल फ़ाइल कैश मेमोरी को फिर से हासिल करते समय, P99 anon + swap पर ~10 से 30 एमबी हेडरूम उपलब्ध कराता है.
com.android.providers.media.module:PhotoPicker पीआईडी (<process>) 85 एमबी + 25 बाइट प्रति पिक्सल
(एचडी+ पर ~110 एमबी, फ़ुल एचडी+ पर ~145 एमबी, ~4.5 एमपी फ़ोल्ड पर ~190 एमबी)
Glide बिटमैप के वर्किंग-सेट नी से मैच करने के लिए, डिसप्ले पिक्सल की संख्या (additionalBytesPerDisplayPixel="25") के हिसाब से, अनुपात में स्केल किया जाता है.
com.android.networkstack यूआईडी (<application>) 65 एमबी 70 एमबी 85 एमबी ज़्यादातर ऐसी मेमोरी जो पहचान ज़ाहिर नहीं करती. इसका साइज़, P95 लाइफ़टाइम memory.peak के हिसाब से तय किया जाता है, ताकि कैप्टिव-पोर्टल टीएलएस, डीएनएस, और डीएचसीपी बर्स्ट को कवर किया जा सके.
com.android.appsearch.apk यूआईडी (<application>) 130 एमबी इसमें लॉक की गई और खाली न की जा सकने वाली करीब 100 एमबी मेमोरी के साथ-साथ, करीब 5 एमबी का हॉट होस्ट वर्किंग सेट शामिल होता है. साथ ही, लॉक किए गए हिस्से को बढ़ाने के लिए हेडरूम भी होता है. कोल्ड पेज कैश मेमोरी (P99 पर ~330 एमबी तक) को फिर से हासिल करने के दौरान, ≥125 एमबी पर शून्य रीफ़ॉल्ट मेज़र किए गए.
com.android.phone (TeleService) पीआईडी (<process>) 120 एमबी शेयर किए गए यूआईडी 1001 पर, हर पीआईडी के लिए स्कोप किया गया. वर्किंग सेट, रैम के बजाय डिवाइस जनरेशन को ट्रैक करता है. इसलिए, एक बजट, हर मॉडल के लिए सबसे ज़्यादा P99 anon + swap से ~30 एमबी ज़्यादा होता है.

ओईएम अपने डिवाइसों के लिए, सिस्टम ऐप्लिकेशन के बजट को फिर से कैसे कैलिब्रेट कर सकते हैं

सिस्टम ऐप्लिकेशन को पसंद के मुताबिक बनाने वाली OEM (उदाहरण के लिए, वेंडर की ब्लूटूथ प्रोफ़ाइलें, पसंद के मुताबिक बनाए गए टेलीफ़ोनी एक्सटेंशन या OEM मीडिया इंडेक्सर जोड़ना) या पहले से इंस्टॉल की गई अतिरिक्त सिस्टम सेवाएं शिप करने वाली OEM, मौजूदा बजट को फिर से तय कर सकती हैं. इसके अलावा, वे अपने हार्डवेयर पोर्टफ़ोलियो के लिए नई *-budget.xml फ़ाइलें भी बना सकती हैं.

ऐक्टिव cgroup v2 मेमोरी प्रोफ़ाइलें इकट्ठा करें

हर टारगेट सिस्टम प्रोसेस के लिए, /sys/fs/cgroup/uid_<uid>/pid_<pid>/ में मौजूद इसके cgroup v2 मेमोरी नोड पढ़ें. ऐसा, अलग-अलग वर्कलोड और रैम टियर के लिए करें:

  • memory.current: cgroup के लिए चार्ज की गई मौजूदा रेज़िडेंट मेमोरी (Zygote-शेयर किए गए पेजों को छोड़कर)
  • memory.peak: प्रोसेस शुरू होने के बाद से, जीवनकाल पीक memory.current हाई-वॉटर मार्क
  • memory.swap.current: ZRAM में कंप्रेस किए गए स्वैप का मौजूदा इस्तेमाल
  • memory.stat: anon, file, pgscan, pgsteal, workingset_refault_anon, और workingset_refault_file का ब्रेकडाउन
  • memory.events: high के उल्लंघन की कुल संख्या (memory.high थ्रॉटलिंग और रिक्लेम इवेंट)

डॉगफ़ूड या फ़ील्ड डिवाइसों से टेलीमेट्री इकट्ठा करते समय, प्रोसेस की चालू स्थितियों (TOP, FOREGROUND_SERVICE, चालू बाइंडिंग, और चालू सिंक) को, इस्तेमाल में न होने वाली CACHED या इस्तेमाल में न होने वाली PERSISTENT स्थितियों से अलग करें. इससे, इस्तेमाल में न होने वाले स्नैपशॉट, P95 और P99 वर्किंग-सेट के हिसाब को कम नहीं करते.

कंप्रेस न किए जा सकने वाले हीप को, फिर से इस्तेमाल किए जा सकने वाले फ़ाइल कैश से अलग करना

anon + swap के 99वें पर्सेंटाइल की तुलना, memory.current के 99वें पर्सेंटाइल से करें:

  • फ़ाइल-कैश मेमोरी पर निर्भर सेवाएं (file &gg; anon, जैसे कि डेटाबेस प्रोवाइडर, Search इंडेक्स करने वाले टूल, और लॉग डीमन): android:maxMb को सेट करें, ताकि P99 anon + swap के मुकाबले ज़्यादा हेडरूम (आम तौर पर 1.5 गुना से 4 गुना) मिल सके. साथ ही, हॉट फ़ाइल पेजों के लिए एक वर्किंग विंडो मिल सके. इस कॉन्फ़िगरेशन की मदद से, कर्नेल लगातार कोल्ड अनमैप किए गए फ़ाइल पेजों को हटाता रहता है. इसके लिए, वह अनाम हीप को नहीं छूता. अगर unevictable या file_mapped बड़ा है (जैसा कि लॉक की गई मेमोरी या मेमोरी-मैप किए गए डेटाबेस के साथ होता है), तो उस फ़ुटप्रिंट को वर्किंग सेट के तौर पर भी गिनें.
  • बिना सोर्स फ़ाइल वाली हीप मेमोरी का ज़्यादा इस्तेमाल करने वाली या अचानक से ज़्यादा मेमोरी इस्तेमाल करने वाली सेवाएं (anon, memory.current का ज़्यादातर हिस्सा बनाती है, जैसे कि नेटवर्किंग या कनेक्टिविटी स्टैक): memory.peak के P90 से P95 को कवर करने के लिए, साइज़ android:maxMb होना चाहिए, ताकि अचानक से होने वाले बर्स्ट, इंतज़ार के समय के हिसाब से संवेदनशील थ्रेड पर सिंक्रोनस ZRAM स्वैप-आउट को ट्रिगर न करें.
  • डिसप्ले-इंटेंसिव यूज़र इंटरफ़ेस (यूआई) प्रोसेस (जैसे, पिकर, गैलरी या सिस्टम यूज़र इंटरफ़ेस (यूआई) हेल्पर): सबसे कम और सबसे ज़्यादा रिज़ॉल्यूशन वाले डिसप्ले पर, बेसलाइन नॉन-यूज़र इंटरफ़ेस (यूआई) फ़ुटप्रिंट और हर मेगापिक्सल के हिसाब से बढ़ते हुए बिटमैप फ़ुटप्रिंट को मेज़र करें. साथ ही, android:maxMb और android:additionalBytesPerDisplayPixel का इस्तेमाल करके बजट को दिखाएं.

फ़िज़िकल डिवाइसों पर memory.high को स्वीप करना

बिल्ड के लिए नया बजट तय करने से पहले, am memory-limiter manual का इस्तेमाल करें (या userdebug बिल्ड पर प्रोसेस के memory.high cgroup नोड पर सीधे लिखें) अपने ऑटोमेटेड सीयूजे टेस्ट चलाते समय, कैंडिडेट की सीमाओं की जांच करने के लिए:

# Override memory.high for a specific PID to 60 MB during a CUJ test
adb shell am memory-limiter manual <PID> 60MB

सीयूजे से पहले और बाद में, memory.stat और memory.events को मॉनिटर करें:

  • बजट बहुत कम है: सीयूजे के दौरान, workingset_refault_anon या workingset_refault_file में अचानक बढ़ोतरी होती है, फ़ोरग्राउंड में चल रहे काम के दौरान memory.swap.current लगातार बढ़ता है या सीयूजे फ़्रेम या बाइंडर लेटेंसी कम हो जाती है. बजट को डिफ़ॉल्ट नी से ज़्यादा बढ़ाएं.
  • बजट बहुत ज़्यादा है: फ़ाइल-स्ट्रीमिंग या इंडेक्स करने के भारी वर्कलोड के बाद भी, memory.events:high 0 ही रहता है. साथ ही, memory.stat में file, कोल्ड पेजों के साथ तेज़ी से बढ़ता है. बजट को कम करके, ऐक्टिव वर्किंग सेट के हिसाब से सेट करें.
  • बजट को सही तरीके से कैलिब्रेट किया गया है: सामान्य कार्रवाई के दौरान, स्टेडी-स्टेट memory.current, memory.high से कम रहता है. ज़्यादा फ़ाइल या कैश मेमोरी के बर्स्ट के दौरान, pgsteal कोल्ड फ़ाइल पेजों को फिर से हासिल करता है. वहीं, workingset_refault_anon शून्य के करीब रहता है और सीयूजे लेटेंसी में कोई बदलाव नहीं होता.