Uygulama bellek bütçeleri, uygulamaların kendileri için bir bellek bütçesi beyan etmesine olanak tanır. Bu bütçe, uygulama belirlenen bütçesinden daha fazla bellek kullandığında sisteme bellek kullanımını azaltmasını söyler. Bu, özellikle sistem uygulamaları ve paket uygulamalar ya da belleği kısıtlı cihazları hedefleyen uygulamalar için kullanışlıdır. Bu durumlarda geliştirici, beklenen bellek çalışma grubunu (uygulamanın, kullanıcının o anda yaptığı işlem için aktif olarak ihtiyaç duyduğu bellek) bilir ve uygulamasının sistemin paylaşılan RAM kaynaklarını çok fazla kullanmamasını ister.
Bütçe, son zamanlarda kullanılmayan bellek sayfalarını kaldırmak için bellek çıkarma ve takas kullanılarak dengede tutulur. Böylece uygulamanın bellekte kapladığı yer, mevcut çalışma grubuna odaklanır. Bir uygulama beyan edilen bütçesini aştığında işletim sistemi, özellikle bu uygulamayı hedefleyerek geri alma işlemi yapar:
- Temiz dosya destekli sayfalar (ör. etkin olmayan kod ve eşlenen öğeler), gerektiğinde depolama alanından yeniden okunabildikleri için ilk olarak çıkarılır.
- Kirli dosya destekli sayfalar depolama alanına geri yazılır ve çıkarılır.
- Anonim bellek sayfaları (ör. yığın ayırmalar) sıkıştırılır ve zRAM'e taşınır.
Çalışma grubu bütçeyi aşmadığı sürece uygulama, bütçesindeki bellekten fazlasını kullanmadan iyi çalışır. İşletim sistemi, kullanılmayan belleği çıkarır ve etkin olmayan yığın sayfalarını takas alanına sıkıştırır. Böylece bellek ayırmaları, işlem sonlandırılmadan sınırlı kalır.
Bir uygulama bütçesini aştığında ne olur?
Bellek bütçesinin aşılması, sistemin uygulamanızı sonlandırmasına veya uygulamanızın kilitlenmesine neden olmaz. Bunun yerine, bir uygulama bütçesinin izin verdiğinden daha fazla bellek kullandığında işletim sistemi, uygulamanın bir süredir kullanmadığı belleği bulur ve tekrar ihtiyaç duyulana kadar güvenli bir şekilde saklar. Bu sayede yeni uygulama bellek ayırmaları için yer açılır ve uygulamanın genel bellek kullanımı bütçe dahilinde kalır.
Uygulamanın herhangi bir zamanda ihtiyaç duyduğu bellek (çalışma grubu) ile belirlenen bütçe arasında boşluk olduğu sürece uygulama normal şekilde çalışmaya devam eder. Bir uygulama, çalışma grubundan daha küçük bir bütçe belirlerse uygulamanın çalışma zamanındaki performansı yavaşlayabilir.
İşletim sisteminin belleği nasıl izlediğini ve geri aldığını öğrenmek için bellek mimarisi kılavuzuna bakın:
- Bellek geri kazanımı ve takas: İşletim sisteminin, özel anonim yığın ve dosya destekli sayfaları uygulamanıza nasıl yüklediğini ve bütçe aşıldığında soğuk sayfaları nasıl geri kazandığını açıklar.
- Temel kavramlar: Paylaşılan Zygote belleğinin neden uygulamanızın bütçesinden hariç tutulduğunu ve yerleşik bütçelerin saha telemetrisindeki anonim RSS + takas ile nasıl ilişkili olduğunu açıklar.
- Bit eşlemler ve bellek: GPU destekli donanım bit eşlemlerinin ve DMA-BUF ayırmalarının, uygulamanızın yığın ve bellek bütçesi dışında nasıl hesaba katıldığını açıklar.
Android manifest dosyasında bütçeleri bildirme
Bellek bütçelerinizi AndroidManifest.xml içinde tanımlamak, bütçeleri tanımlamak için birincil ve önerilen yöntemdir. Çalışma zamanı kodu gerektirmez, işlem başlatıldığında hemen geçerli olur ve işletim sistemi için net bir sözleşme sağlar.
<memory-budget> beyanları, Android 17 QPR2 (API düzeyi 37.2) ve sonraki sürümleri çalıştıran cihazlarda geçerli olur. Daha eski Android sürümlerinde platform manifest ayrıştırıcısı, tanınmayan XML öğelerini güvenli bir şekilde yoksayar. Bu sayede, geriye dönük uyumluluğu etkilemeden <memory-budget> öğesini kullanabilirsiniz.
Referans bütçe belirleme
Çoğu uygulama için tek bir bütçe tanımlamak yeterlidir. Doğrudan <application> etiketinin içinde bir <memory-budget> öğesi tanımlayın:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.simpleapp">
<application
android:label="@string/app_name">
<!-- Baseline budget for the application -->
<memory-budget android:maxMb="256" />
</application>
</manifest>
Bu, paket için tüm işlemler ve durumlarda 256 MB'lık yerleşik bellek bütçesi belirler. Uygulamanın bellekte kapladığı yer 256 MB'ı aştığında işletim sistemi, çıkarma ve takas yöntemlerini kullanarak etkin olmayan bellek sayfalarını kırpar.
Bütçeleri işlem durumuna göre değiştirme
Bir uygulamanın ihtiyaç duyduğu bellek miktarı, kullanıcı görünürlüğüne bağlı olarak değişir:
- Ön plan: İşlem, kullanıcıyla etkileşimde bulunan görünür bir etkinliği barındırır. Bu durum, etkin kullanıcı arayüzü ve grafikler nedeniyle genellikle en büyük ayak izine sahiptir.
- Algılanabilir: İşlem, kullanıcı tarafından algılanabilir ancak görünür bir pencere barındırmaz (örneğin, medya oynatma ön plan hizmeti, etkin bir arka plan indirme işlemi, adım adım rota veya etkin bir giriş yöntemi barındırma).
- Arka plan: İşlem, arka plan işlerini, alıcıları veya veri senkronizasyonlarını çalıştırıyor. Minimum düzeyde yer kaplaması beklenir.
Bu durumlarla eşleşecek birden fazla <memory-budget> ifadesi tanımlayabilirsiniz:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.simpleapp">
<application
android:label="@string/app_name">
<!-- Default budget for visible foreground UI -->
<memory-budget android:maxMb="200" />
<!-- Tighter budget when playing audio in background -->
<memory-budget
android:maxMb="120"
android:state="perceptible" />
<!-- Minimal budget when fully in background -->
<memory-budget
android:maxMb="48"
android:state="background" />
</application>
</manifest>
android:state içermeyen bir temel ifade gerekli değildir. Yalnızca eyalete özgü ifadeler (ör. android:state="background") belirtirseniz diğer eyaletler uygulama bütçesiyle kısıtlanmaz. Dahil edildiğinde, android:state içermeyen bir ifade, belirtilmemiş durumlar (ör. ön plan) için varsayılan yedek görevi görür. Uygulama perceptible veya background durumlarına geçtiğinde, daha kısıtlayıcı olan sonraki ifadeler bu ifadeyi geçersiz kılar.
Her bir durum için bütçe boyutunu belirlerken, en yüksek yerleşik bellek (RSS üst sınır) yerine o durumun etkin çalışma grubunu hedefleyin:
- Sorunsuz kare oluşturma için
foregroundbütçelerini boyutlandırın: Ön plan kullanıcı arayüzüne, etkin çalışma grubu için yeterli alan verin. Böylece eşzamanlı geri alma, kare oluşturmayı geciktirmez. Uygulamanız yoğun bellek kullanan bağımsız bir iş akışı (ör. yüksek çözünürlüklü resim veya video düzenleyici) içeriyorsa bu etkinliği özel bir işleme taşıyabilirsiniz (Çok işlemli uygulamalar bölümüne bakın). Böylece, tahsisatları bağımsız olarak bütçelendirilir ve kullanıcı çıktığında kaldırılır. - Başsız işlemler için bütçeleri sıkı bir şekilde
backgroundboyutlandırın: Saha telemetrisinde yüksek arka plan RSS'si genellikle ön plandan ayrıldıktan sonra korunan kullanıcı arayüzü ayırmalarını (Bağlı bir hizmeti, bellek yoğun bir kullanıcı arayüzüyle aynı yere yerleştirme ve Etkinliklere yanıt olarak belleği serbest bırakma bölümlerine bakın) veya arka plan dizine ekleme ve senkronizasyon sırasında tek seferlik dosya okumalarını yansıtır. Başsız arka plan çalışmasının kare son tarihleri olmadığından, arka plan senkronizasyonu bütçesini kısa süreliğine aştığında tek seferlik dosya önbelleği sayfalarının çıkarılması ve etkin olmayan yığın sayfalarının zRAM'e taşınması, kullanıcı tarafından görülebilen takılmalara neden olmaz ve arka plandaki ani artışların kullanıcının ön plandaki uygulamasını çıkarmasını önler.
Bütçe durumlarının işlem durumlarıyla eşleşmesi
Platform, manifest bütçe durumlarını çalışma zamanı işlem durumlarıyla eşler. Bu eşleme, Activity Manager'ın hem işlemin kendi etkin bileşenlerinden hem de diğer uygulamalardan gelen hizmet bağlamaları veya içerik sağlayıcı sorgularından hesapladığı RunningAppProcessInfo.importance değerine göre yapılır:
foreground: Devam ettirilen bir etkinliği barındırma, ekran kapandığında en üstteki uygulama durumunu koruma veya en üstteki ön planda çalışan uygulama tarafından etkin bir şekilde bağlı ya da sorgulanan bir hizmeti veya içerik sağlayıcıyı barındırma gibi etkin, görünür kullanıcı etkileşimleri.perceptible: Görünür bir pencere olmadan kullanıcı tarafından algılanabilen iş yükleri. Örneğin, etkin medya oynatma, adım adım rota, kamera veya mikrofonla kayıt, etkin arka plan indirmeleri, veri senkronizasyonu ön plan hizmetleri veyaBIND_NOT_FOREGROUNDgibi işaretler kullanılarak ön plan istemcisi tarafından bağlanan hizmetler (BIND işaretleriyle devralmayı kontrol etme bölümüne bakın). Dahili platform metrikleri bu iş yüklerinden bazılarınıPROCESS_STATE_IMPORTANT_FOREGROUNDolarak etiketleyebilir ancak bu dahili sabit, görünür bir pencereyi ifade etmez veperceptiblebütçesi tarafından yönetilir.background: Kullanıcı tarafından hemen fark edilemeyen işler. Örneğin, arka plan işleri, alarmlar, yayın alıcıları veya kullanıcı başka bir yere gittikten sonra önceki etkinlik.
Aşağıdaki tabloda, çalışma zamanı önem düzeylerinin manifest durumlarıyla nasıl eşleştiği gösterilmektedir:
Manifest android:state |
Çalışma zamanı önemi (RunningAppProcessInfo) |
Tipik bileşenler |
|---|---|---|
foreground |
IMPORTANCE_FOREGROUNDIMPORTANCE_TOP_SLEEPING |
Devam ettirilen görünür etkinlik, ekranı kilitli olan en üstteki uygulama, en üstteki uygulamaya bağlı hizmet veya içerik sağlayıcı |
perceptible |
IMPORTANCE_FOREGROUND_SERVICEIMPORTANCE_VISIBLE |
Etkin medya oynatma, navigasyon, indirme veya senkronizasyon ön plan hizmetleri; ön plan hizmeti veya görünür işaretlerle bağlı hizmet |
background |
IMPORTANCE_PERCEPTIBLEIMPORTANCE_CANT_SAVE_STATEIMPORTANCE_SERVICE |
Arka plan işleri, alıcılar, arka plan senkronizasyonu, önceki etkinlik veya arka plandaki ana sayfa uygulaması |
İstemci bağlamalarının bir hizmet veya sağlayıcı ana makine işlemini nasıl yükselttiği ve istemci bağlantıyı kaldırdıktan ya da ekran dışına çıktıktan sonra işlemin nasıl tekrar background veya cached durumuna geçtiği hakkında ayrıntılı bilgi için Hizmet bağlamaları ve işlem durumları başlıklı makaleyi inceleyin.
Bir işlem, etkin bileşenleri olmadan önbelleğe alınmış duruma (IMPORTANCE_CACHED) girdiğinde platform, etkin bellek bütçesini serbest bırakır ve belleğini standart önbelleğe alınmış işlem geri alma işlemiyle yönetir.
Uygulamanızın işlem durumunu ve bütçesini inceleme
Geliştirme sırasında uygulamanızın etkin işlem durumunu ve önemini incelemenin bir yolu, ADB'yi kullanarak Etkinlik Yöneticisi'ni sorgulamaktır:
adb shell dumpsys activity processes <package-name>
Aşağıdaki kısaltılmış örnekte, ön plan hizmeti çalıştıran bir uygulamanın işlem kaydı ve OOM kontrol girişleri gösterilmektedir:
ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)
All known processes:
*APP* UID 10123 ProcessRecord{edf056c 3919:com.example.app/u0a123}
pid=3919
oom adj: max=1001 curRaw=200 setRaw=200 cur=200 set=200
curProcState=4 mRepProcState=4 setProcState=4 lastStateTime=-42s360ms
hasStartedServices=true
mHasForegroundServices=true forcingToImportant=null
...
Process OOM control (48 total):
Proc #22: prcp F/S/FGS ---NFU-TI t: 0 3919:com.example.app/u0a123 (fg-service)
oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200
state: cur=FGS set=FGS lastRss=0.00 lastCachedRss=0.00
Bu çıktıda:
curProcState=4vestate: cur=FGS, işlemin ön plan hizmeti durumunda olduğunu gösterir. Uygulamanız etkin ve görünür bir etkinlik barındırıyorsa bu durumTOPolarak gösterilir. Önemli bir ön plan bileşeni (ör. indirme veya senkronizasyon) çalıştırıyorsaIMPFolarak görünür.- İşlem OOM kontrol tablosunda
prcp, işleminperceptiblebütçesine karşılık gelen algılanabilir öncelik katmanı altında değerlendirildiğini gösterir.
Şu anda uygulanan bellek bütçesini ve yerleşik bellek kullanımını incelemek için:
adb shell dumpsys meminfo <package-name>
Android 17 QPR2'den itibaren bu çıkışta, etkin sınır tavanını, sınır kaynağını (ör. AndroidManifest veya MemoryBudgetManager) ve mevcut yerleşik belleği gösteren bir Bellek Bütçesi bölümü yer alır.
Çok işlemli uygulamalar
Uygulamanız işini birden fazla işleme bölüyorsa <processes> içindeki <process> etiketini kullanarak özel işlem bütçeleri yapılandırın.
Örneğin, bir müzik yayını uygulaması (com.example.radio) düşünün:
- Ana işlem: Görünür kullanıcı arayüzünü ve ses çalma motorunu barındırır
(
MediaSessionService,mediaPlaybackön plan hizmetiyle). Görünür olduğunda işlem, 180 MB ön plan bütçesiyle çalışır. Kullanıcı müzik çalmaya devam ederken uygulamadan ayrıldığında işlemperceptibledurumuna girer. Bu durumda, oynatma motoru ve ses arabelleği için 64 MB bütçe yeterlidir. - Senkronizasyon işlemi (
:sync): Arka planda meta veri senkronizasyonu ve indirme dizine ekleme işlemlerini çalıştıran özel işlem. Bu işlem yalnızca arka planda etkin olduğundanstate="background"değerini açıkça belirtmeniz gerekmez. Tek bir bütçe geçerlidir.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.radio">
<application
android:label="@string/app_name">
<!-- Package baseline: main process with UI and audio playback -->
<memory-budget android:maxMb="180" />
<!-- Tighter budget when audio plays in the background -->
<memory-budget
android:maxMb="64"
android:state="perceptible" />
<processes>
<!-- Dedicated background sync process -->
<process android:process=":sync">
<memory-budget android:maxMb="32" />
</process>
</processes>
<service
android:name=".playback.AudioPlayerService"
android:foregroundServiceType="mediaPlayback"
android:exported="false" />
<service
android:name=".sync.PlaylistSyncService"
android:process=":sync"
android:exported="false" />
</application>
</manifest>
Manifestinizde bir <processes> bloğu beyan ettiğinizde, yalnızca <memory-budget> beyan eden işlemler için <process> girişlerini listeleyin. Uygulamanın geri kalanını cihazın sistem sınırında bırakırken yalnızca belirli alt işlemler için bütçe belirlemek istiyorsanız doğrudan <application> altındaki <memory-budget> öğelerini atlayabilirsiniz.
Herhangi bir alt işlemdeki bellek kullanımı, hem kendi işlem bütçesine hem de kapsayıcı paket bütçesine dahil edilir. Bir işlem, çalışma zamanında işlem bütçesini veya paket bütçesini (hangisine önce ulaşılırsa) aşarsa bellek baskısı yaşar.
Yüksek yoğunluklu ekranlar için bütçeleri ölçeklendirme
Bellekte kapladığı yer, ekranda aynı anda çizilecek piksel sayısına göre önemli ölçüde değişen uygulamalar (ör. ekran boyutunda bitmap'leri önbelleğe alan bir fotoğraf galerisi uygulaması) için Android, bütçeleri ekran özelliklerine göre dinamik olarak ölçeklendiren iki alternatif mekanizma sunar:
Ekran yoğunluğu grubuna göre ölçeklendirme (
android:additionalMbPerDensity): 2D raster çizilebilir öğeler ve kullanıcı arayüzü öğeleri hem genişlik hem de yükseklik olarak ölçeklendirildiğinden, ekranınmdpi(1,0x / 160 dpi) değerine göre yoğunluk oranının karesiyle orantılı olarak megabayt ekler:<!-- Baseline 180MB + 16MB multiplied by (density ratio)^2 --> <memory-budget android:maxMb="180" android:additionalMbPerDensity="16" />mdpiekranda (1,0x) bütçe 180 + 16 × 12 = 196 MB'tır.xxhdpiekranda (3,0x), bütçe 180 + 16 × 32 = 324 MB'a ölçeklenir.Fiziksel ekran çözünürlüğüne göre ölçeklendirme (
android:additionalBytesPerDisplayPixel): Baytları doğrudan fiziksel ekran pikseli (Genişlik × Yükseklik) başına ekler. Bu, tam ekran grafik yüzeyleri, oluşturma arabellekleri veya tam çözünürlüklü fotoğraf önbellekleri ayıran uygulamalar için idealdir. Bu uygulamalarda bellek tüketimi, kullanıcı arayüzü yoğunluk grupları yerine doğrudan ham ekran piksel sayısıyla ölçeklenir:<!-- Baseline 128MB + 16 bytes per physical display pixel --> <!-- For example, a 4-byte RGBA full-screen buffer with double or quadruple buffering --> <memory-budget android:maxMb="128" android:additionalBytesPerDisplayPixel="16" />1080p ekranda (1080 × 2400 ≈ 2,59 milyon piksel) bu, temel bütçeye yaklaşık 41,4 MB ekler. 1440p ekranda (1440 × 3120 ≈ 4,49 milyon piksel) ≈ 71,8 MB ekler.
Bu iki özellik birbirinin alternatifidir. Uygulamanızın birincil ölçeklendirme faktörüyle eşleşen özelliği seçin ve her ikisini aynı maddede birleştirmekten kaçının.
Cihaz form faktörleri için özelleştirme
Telefonlar, tabletler ve Wear OS için APK gönderirken farklı donanım hedeflerine yönelik bütçeleri ayarlamak için android:feature özelliğini kullanın.
Wear OS kol saatlerinde RAM sınırlıdır ve uygulamanın kullanıcı arayüzü ile özellik seti çok daha basittir. watch özelliği için daha sıkı bir bütçe belirleyebilirsiniz:
<!-- General phone and tablet baseline -->
<memory-budget android:maxMb="180" />
<!-- Wear OS override: simpler UI and constrained hardware -->
<memory-budget
android:maxMb="48"
android:feature="watch" />
Çözüm kuralı: Geçerli son madde yürürlüğe girer
Bir uygulama veya işlem için birden fazla <memory-budget> öğesi tanımlandığında sistem, bunları manifest dosyasında belirtildikleri sıraya göre değerlendirir. Geçerli olan son bütçe maddesi, uygulanan maddedir.
Geçerli olan son bütçe geçerli olacağından sıralama önemlidir. Her zaman en genel temel bütçeyi ilk sıraya koyun ve ardından daha spesifik geçersiz kılmaları (eyalete özgü veya donanıma özgü maddeler gibi) ekleyin.
XML özelliği referansı
Tüm bellek boyutu özellikleri megabayt (MB) cinsinden ifade edilir ve Linux cgroup memory.current şarjıyla (Zygote gibi paylaşılan bellek hariç) eşlenir.
| Özellik | Biçim | Varsayılan | Açıklama |
|---|---|---|---|
android:maxMb |
Tam sayı (> 0) | Zorunlu | MB cinsinden temel yerleşik bellek bütçesi sınırı. |
android:state |
Enum | Tümü | Bu bütçenin geçerli olduğu işlem durumu: foreground, perceptible veya background. Atlanırsa bu ifade, belirtilmemiş durumlar için yedek görevi görür. |
android:additionalMbPerDensity |
Tam sayı (≥ 0) | 0 |
mdpi (1,0x) ile karşılaştırıldığında ekran yoğunluğu oranının karesiyle çarpılan ek megabaytlar. |
android:additionalBytesPerDisplayPixel |
Tam sayı (≥ 0) | 0 |
Fiziksel ekran pikseli (Genişlik × Yükseklik) başına ayrılan ek baytlar. Yüzey arabellekleri ve bit eşlemler için kullanışlıdır. |
android:feature |
Dize | Tümü | Şartı, belirli donanım özelliklerini bildiren cihazlarla kısıtlar: watch, automotive veya leanback. |
Çalışma zamanı API'leri (ikincil dinamik seçenek)
Bütçeleri AndroidManifest.xml içinde statik olarak bildirmek, neredeyse tüm uygulamalar için tercih edilen çözümdür. Ancak Android, dinamik iş yükleri olan uygulamalar veya çalışma zamanı denemeleri için ikincil bir seçenek olarak çalışma zamanı SDK'sı ve NDK API'leri sunar.
Çalışma zamanı API'si şunları yapmanıza olanak tanır:
- Mevcut bellek kullanımını ve geçerli bütçeleri sorgulama
- İşlem bütçenizi dinamik olarak azaltın.
- İşletim sistemi doğrudan geri alma işlemini tetiklemeden önce önbellekleri proaktif olarak kırpmak için bütçe aşımı etkinliklerini dinleyin.
Android SDK API (MemoryBudgetManager)
MemoryBudgetManager sistem hizmeti, Android 17 QPR2'den (küçük SDK sürümü, API düzeyi 37.2 / Build.VERSION_CODES_FULL.CINNAMON_BUN_2) itibaren Kotlin ve Java dillerinde yazılmış uygulamalarda kullanılabilir.
Hizmeti alma
MemoryBudgetManager özelliğine erişmeden önce SDK_INT_FULL bölümünü kontrol ederek cihazda Android 17 QPR2 veya sonraki bir sürümün yüklü olduğundan emin olun. Uygulamanızın minSdk değeri 36'dan düşükse Android 15 (API düzeyi 35) veya daha eski sürümleri çalıştıran cihazların SDK_INT_FULL değerini okurken NoSuchFieldError hatası vermemesi için önce SDK_INT değerini kontrol edin:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA &&
Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_2
) {
val budgetManager = context.getSystemService(MemoryBudgetManager::class.java)
}
Sorgu kullanımı ve bütçeler
// Query current memory charged to this process and the package UID
val processUsageBytes = budgetManager.processCurrentUsageBytes
val packageUsageBytes = budgetManager.packageCurrentUsageBytes
// Query effective budgets (returns LIMIT_IS_DISABLED if unconstrained)
val processBudgetBytes = budgetManager.processBudgetBytes
val packageBudgetBytes = budgetManager.packageBudgetBytes
Bütçeleri dinamik olarak ayarlama veya temizleme
Hafif görevler sırasında belleği kısıtlamak için çalışma zamanında daha sıkı bir bütçe belirleyebilir veya görev bittiğinde bütçeyi temizleyebilirsiniz:
// Set a tighter dynamic budget on the current process (e.g., 96 MB)
try {
budgetManager.processBudgetBytes = 96L * 1024L * 1024L
} catch (e: IllegalArgumentException) {
// Thrown if the budget is <= 0 or exceeds the manifest ceiling or system limit
Log.e(TAG, "Requested budget exceeds manifest or system ceiling", e)
}
// Clear the dynamic process budget to restore the manifest limit (or unconstrained baseline)
budgetManager.clearProcessBudget()
İşlem foreground, perceptible ve background durumları arasında geçiş yaptığında bütçeleri otomatik olarak değiştiren <memory-budget> manifest yan tümcelerinin aksine, MemoryBudgetManager aracılığıyla ayarlanan dinamik bütçe, kodunuz güncellenene veya temizlenene kadar tek bir üst sınır uygular. Saha denemeleri yapmak veya duruma özgü sınırları yönetmek için çalışma zamanı API'sini kullanıyorsanız yaşam döngüsü geçişleri genelinde dinamik bütçeyi güncelleyin veya temizleyin (örneğin, Uyarlanabilir resim düzenleyici örneğinde gösterildiği gibi ProcessLifecycleOwner veya etkinlik yaşam döngüsü geri çağırma yöntemlerini kullanarak).
Performans ve eş kuvvetlilik
Çalışma zamanında MemoryBudgetManager ile çalışırken aşağıdaki özellikleri göz önünde bulundurun:
- Idempotency: Tüm bütçe mutasyon API'leri idempotenttir.
clearProcessBudget()veyaclearPackageBudget()işlevini tekrar tekrar ya da etkin bir dinamik bütçe yokken çağırmak güvenli bir işlem yapmama durumudur. Benzer şekilde,setProcessBudgetBytes()veyasetPackageBudgetBytes()işlevini art arda aynı değere ayarlamak, gereksiz sistem yeniden yapılandırmalarını tetiklemez. - Çağrı sıklığı: Bütçe ayarlama, güncelleme veya temizleme işlemleri, işlemin işletim sistemi bellek denetleyicisini güncellemek için sisteme ait hizmete çapraz işlem çağrısı yapar. Hafif olsa da tamamen ücretsiz değildir. Bu API'leri ayrı yaşam döngüsü geçişleri ve görev aşamaları sırasında (ör. bir etkinliğe girerken veya etkinlikten çıkarken, arka plan işlemeyi başlatırken veya tamamlarken ya da konuşma isteklerini işlerken) çağırın ve dar döngülerde, ses işleme iş parçacıklarında veya kare başına oluşturma rutinlerinde çağırmaktan kaçının.
- Sorgu sıklığı:
processCurrentUsageBytesvepackageCurrentUsageBytessorgu özellikleri, sistem hizmetinden canlı bellek kullanımını getirir. Sorgular hızlı olsa da sürekli döngüler halinde sorgulanmak yerine isteğe bağlı olarak (ör. görev geçişleri sırasında veyaOnOverBudgetListenergeri çağırmaları içinde) çağrılmalıdır.
Bütçe aşımı baskısı geri çağırmalarını dinleme
Uygulamalar, bellek kullanımı bütçe eşiğini aştığında bildirim almak için bir dinleyici kaydedebilir. Bu sayede uygulama, işletim sistemi işlemi veya paketi bütçesi dahilinde tutmak için belleği geri alırken uygulama düzeyinde proaktif temizleme (bellek içi bitmap önbelleklerini temizleme gibi) gerçekleştirebilir:
val cleanupThread = HandlerThread("MemoryBudgetCleanup").apply { start() }
val listener = MemoryBudgetManager.OnOverBudgetListener { budgetBytes ->
Log.w(TAG, "Process exceeded memory budget of $budgetBytes bytes")
// Proactively evict thread-safe caches on the background Looper
imageTileCache.evictAll()
}
// Register on a background Looper before tightening a dynamic budget
budgetManager.registerProcessOverBudgetListener(cleanupThread.looper, listener)
// When done (for example, in onStop)
budgetManager.unregisterProcessOverBudgetListener(listener)
Bütçe aşımı geri aramaları için en iyi uygulamalar:
- Arka plan kullanın
Looper: Mümkün olduğundaregisterProcessOverBudgetListenerveyaregisterPackageOverBudgetListeneriçin arka planHandlerThreadLooperiletin. Önbellek boşaltma veya dosya temizleme işlemlerini arka plan iş parçacığında çalıştırmak, işlem bellek baskısı altındayken kullanıcı arayüzü iş parçacığında kare oluşturma ile çakışmayı önler.Looper.getMainLooper, yalnızca ana iş parçacığına bağlı kullanıcı arayüzü durumuna dokunan dinleyiciler için ayrılmıştır. - Tekrarlanan geri çağırmalar bekleyin: Bütçe aşımı geri çağırmaları tek seferlik bildirimler değildir. Kullanım bütçeyi aştığında işletim sistemi önce soğuk sayfaları geri alır. Baskı devam ederse sistem, belleği geri almaya ve dinleyicinizi saniyede en fazla bir kez çağırmaya devam eder. Geri çağırma işlevinizi eş güçlü yapın. Uygulamanızın yalnızca tek bir bildirime ihtiyacı varsa veya temizleme işlemleri arasında daha uzun bir bekleme süresi istiyorsa geri çağırma içinde
unregisterProcessOverBudgetListener(veyaunregisterPackageOverBudgetListener) işlevini çağırın ya da temizleme işleminizi bir bekleme süresi zaman damgası veyaAtomicBooleanile koruyun. - Bütçeyi daraltmadan önce kaydolun:
setProcessBudgetBytesveyasetPackageBudgetBytesyöntemini çağırdığınızda mevcut bellek kullanımı yeni bütçeyi zaten aşıyorsa yeni bütçe uygulanır uygulanmaz bütçe aşımı etkinliği tetiklenir. Uygulamanızın ilk bütçe aşımı bildirimini kaçırmaması için bütçeyi ayarlamadan önce dinleyicinizi kaydedin. - Hızlı olun: Geri alma işlemleri anında rahatlama sağlamalıdır. Yük altındayken yapılan karmaşık hesaplamalar performansı düşürür.
- Tahsislerden kaçının: Geri çağırma içinde yeni nesneler tahsis etmeyin veya yeni iş parçacıkları başlatmayın. Aksi takdirde, işletim sisteminin doğrudan geri alma işlemi hemen tetiklenebilir.
- Yüksek verimli hedeflere odaklanın: Büyük bit eşlemleri, oluşturma arabelleklerini çıkarmak veya bellekle eşlenen dosyaları kapatmak, çok sayıda küçük nesneyi serbest bırakmaktan çok daha etkilidir.
Yerel NDK API'si (<android/memory_budget_manager.h>)
Yerel uygulamalar, Android 17 QPR2'den (API düzeyi 37.2) itibaren libandroid.so tarafından sunulan C NDK API'sini kullanabilir.
CMake yapılandırması
find_library(android-lib android)
target_link_libraries(my_native_engine PRIVATE ${android-lib})
Üstbilgi ve sorgu kullanımını dahil etme
#include <android/memory_budget_manager.h>
// Query current memory usage
int64_t process_usage = AMemoryBudgetManager_getProcessCurrentUsageBytes();
int64_t package_usage = AMemoryBudgetManager_getPackageCurrentUsageBytes();
// Query current budget
int64_t process_budget = 0;
AMemoryBudgetResult result = AMemoryBudgetManager_getProcessBudget(&process_budget);
if (result == AMEMORY_BUDGET_RESULT_SUCCESS) {
// Current budget available in process_budget
} else if (result == AMEMORY_BUDGET_RESULT_LIMIT_IS_DISABLED) {
// No budget is currently active
}
Yerel bütçeyi dinamik olarak yapılandırma
// Set a tighter process budget (e.g. 160MB)
AMemoryBudgetResult result = AMemoryBudgetManager_setProcessBudget(160LL * 1024 * 1024);
if (result != AMEMORY_BUDGET_RESULT_SUCCESS) {
const char* error_msg = AMemoryBudgetManager_resultToString(result);
// Handle error (e.g. AMEMORY_BUDGET_RESULT_ERROR_EXCEEDS_MANIFEST_LIMIT)
}
// Clear the dynamic budget to resume manifest limits (or unconstrained baseline)
AMemoryBudgetManager_clearProcessBudget();
Bellek baskısı etkinliklerini izleme
NDK, bellek etkinliklerini izlemenin iki yolunu sunar:
- Üst Düzey İzleyici (
AMemoryBudgetManager_Watcher_create): Otomatik ardışık tekrar önleme ileALooperüzerindeki etkinlikleri izler. - Düşük Seviyeli Dosya Tanımlayıcı:
AMemoryBudgetManager_getProcessMemoryPressureFd, doğrudan özel birepollmotor döngüsüne entegre edilebilen yerel bir dosya tanımlayıcı döndürür.
void onMemoryPressure(int32_t event_mask, const AMemoryBudgetEvents* events, void* userdata) {
// High-yield eviction of unused native textures or geometry caches
purgeNativeTextureCaches();
}
// Register watcher on a background worker ALooper with a 1000ms debounce interval
AMemoryBudgetManagerWatcher* watcher = AMemoryBudgetManager_Watcher_create(
background_looper,
AMEMORY_BUDGET_MANAGER_EVENT_PROCESS,
1000 /* debounce_ms */,
&onMemoryPressure,
NULL /* userdata */
);
// When done:
AMemoryBudgetManager_Watcher_destroy(watcher);
İzleyiciyi ana kullanıcı arayüzünüze veya oluşturma iş parçacığınıza değil, arka plandaki çalışan iş parçacığının ALooper öğesine bağlayın. Böylece önbellek çıkarma işlemi, kare oluşturma ile çakışmaz. Uygulamanızın ilk bütçe aşımı etkinliğini kaçırmaması için bütçeyi AMemoryBudgetManager_setProcessBudget veya AMemoryBudgetManager_setPackageBudget ile sıkılaştırmadan önce izleyiciyi oluşturun.
İşlem veya paket bütçeyi aşmaya devam ederken ve işletim sistemi belleği geri almaya devam ederken izleyici, geri çağırmayı tekrar tekrar çağırır (debounce_ms aralığında en fazla bir kez). Geri çağırmanızı idempotent yapın veya uygulamanızın yalnızca tek seferlik bir bildirime ihtiyacı varsa AMemoryBudgetManager_Watcher_destroy (geri çağırma içinden çağrılması güvenlidir) yöntemini çağırın.
Runtime API örnekleri
Aşağıdaki örneklerde, Android SDK API (Kotlin dilinde yazılmış) ve yerel NDK API (C++ dilinde yazılmış) kullanılarak çalışma zamanı API'lerinin nasıl uygulanacağı gösterilmektedir.
Android SDK örneği: Uyarlanabilir resim düzenleyici
Bu örnekte, çok katmanlı tuvalini barındırmak için manifest dosyasında 256 MB'lık bir üst sınır beyan eden bir resim düzenleme uygulaması (com.example.imageeditor) gösterilmektedir:
<manifest ... >
<application ... >
<!-- Manifest ceiling accommodates the heaviest editing workload -->
<memory-budget android:maxMb="256" />
</application>
</manifest>
Kullanıcı basit küçük resim galerisine göz atarken uygulama, Kotlin'deki Android SDK API'sini kullanarak işlem bütçesini dinamik olarak 96 MB'a düşürür.
Kullanıcı çok katmanlı düzenleme tuvalini açtığında uygulama, dinamik bütçeyi temizleyerek 256 MB'lık manifest tavanını tamamen geri yükler. Ayrıca, kullanıcı arayüzü iş parçacığını engellemeden, baskı altında önbelleğe alınmış önizleme bit eşlemlerini çıkarmak için galeri bütçesini uygulamadan önce arka plandaki HandlerThread
Looper üzerinde bir OnOverBudgetListener kaydeder.
package com.example.imageeditor.ui
import android.app.Activity
import android.app.MemoryBudgetManager
import android.graphics.Bitmap
import android.os.Bundle
import android.os.HandlerThread
import android.util.Log
import android.util.LruCache
class ImageEditorActivity : Activity() {
private lateinit var budgetManager: MemoryBudgetManager
private lateinit var cleanupThread: HandlerThread
// Thread-safe in-memory cache for rendered preview tiles (32MB limit)
private val previewCache = object : LruCache<String, Bitmap>(32 * 1024 * 1024) {
override fun sizeOf(key: String, value: Bitmap): Int = value.byteCount
}
private val overBudgetListener = MemoryBudgetManager.OnOverBudgetListener { budgetBytes ->
Log.w(TAG, "Process memory pressure detected (budget: ${budgetBytes / 1048576}MB). Evicting preview cache.")
previewCache.evictAll()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
budgetManager = getSystemService(MemoryBudgetManager::class.java)
cleanupThread = HandlerThread("MemoryBudgetCleanup").apply { start() }
}
override fun onStart() {
super.onStart()
// Register listener on a background Looper before tightening the budget
budgetManager.registerProcessOverBudgetListener(cleanupThread.looper, overBudgetListener)
// Constrain memory during lightweight gallery browsing
applyGalleryBudget()
}
override fun onStop() {
super.onStop()
budgetManager.unregisterProcessOverBudgetListener(overBudgetListener)
}
override fun onDestroy() {
cleanupThread.quitSafely()
super.onDestroy()
}
/**
* Called when the user enters the high-resolution editing canvas.
* Clears the dynamic budget, restoring the full 256MB manifest ceiling.
*/
fun enterEditingCanvas() {
// Clear the tighter dynamic budget to restore the full manifest ceiling (256MB)
budgetManager.clearProcessBudget()
Log.i(TAG, "Restored manifest budget ceiling (256MB) for editing canvas")
}
/**
* Called when the user exits the editor back to the thumbnail gallery.
* Re-applies the tighter dynamic budget.
*/
fun exitToGallery() {
previewCache.trimToSize(8 * 1024 * 1024)
applyGalleryBudget()
}
private fun applyGalleryBudget() {
try {
// Dynamically tighten budget to 96MB for the lightweight gallery view
budgetManager.processBudgetBytes = 96L * 1024L * 1024L
Log.i(TAG, "Tighter dynamic budget applied for gallery: 96MB")
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Could not apply dynamic budget", e)
}
}
companion object {
private const val TAG = "ImageEditor"
}
}
NDK C++ örneği: Yerel 3D motoru
Bu örnekte, uygulamanın manifest dosyasının yüksek kaliteli grafiklere (android:maxMb="512") uyum sağlamak için 512 MB'lık bir tavan beyan ettiği varsayılarak, etkin grafik kalitesi düzeyine göre bellek bütçelerini yöneten yerel bir C++ oyun motoru gösterilmektedir. Motor, daha düşük kaliteli hazır ayarlar için bütçeyi sıkılaştırmadan önce arka plandaki bir çalışan iş parçacığının ALooper üzerinde AMemoryBudgetManager_Watcher_create işlemini başlatır ve bütçe aşıldığında doku mipmap'lerini kaldırır.
#include <android/memory_budget_manager.h>
#include <android/looper.h>
#include <android/log.h>
#define LOG_TAG "Native3DEngineMemory"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
class MemoryGovernor {
public:
MemoryGovernor() : mWatcher(nullptr) {}
~MemoryGovernor() {
stopMonitoring();
}
// Configures process budget based on user graphics quality settings
bool setQualityBudget(int qualityLevel) {
int64_t targetBytes = 0;
switch (qualityLevel) {
case 0: // Low (budget: 128MB)
targetBytes = 128LL * 1024 * 1024;
break;
case 1: // Medium (budget: 256MB)
targetBytes = 256LL * 1024 * 1024;
break;
case 2: // High (budget: 512MB)
targetBytes = 512LL * 1024 * 1024;
break;
default:
// Clear dynamic override and restore manifest limit
AMemoryBudgetManager_clearProcessBudget();
return true;
}
AMemoryBudgetResult result = AMemoryBudgetManager_setProcessBudget(targetBytes);
if (result != AMEMORY_BUDGET_RESULT_SUCCESS) {
LOGW("Could not set quality budget: %s", AMemoryBudgetManager_resultToString(result));
return false;
}
return true;
}
// Call on a background worker ALooper before tightening the budget with setQualityBudget
bool startMonitoring(ALooper* backgroundLooper) {
if (!backgroundLooper) return false;
// Monitor process budget events, debounced to at most once every 1000ms
mWatcher = AMemoryBudgetManager_Watcher_create(
backgroundLooper,
AMEMORY_BUDGET_MANAGER_EVENT_PROCESS,
1000,
&MemoryGovernor::onPressureEvent,
this
);
return mWatcher != nullptr;
}
void stopMonitoring() {
if (mWatcher) {
AMemoryBudgetManager_Watcher_destroy(mWatcher);
mWatcher = nullptr;
}
}
void unloadUnusedTextures() {
LOGW("Memory pressure callback triggered. Purging cached texture mipmaps...");
// Fast, idempotent, high-yield eviction without allocating memory
}
private:
static void onPressureEvent(
int32_t event_mask,
const AMemoryBudgetEvents* events,
void* userdata
) {
auto* governor = static_cast<MemoryGovernor*>(userdata);
governor->unloadUnusedTextures();
}
AMemoryBudgetManagerWatcher* mWatcher;
};