Birçok uygulama, kullanıcı arayüzü artık görünür olmadığında meşru işler yapar. Örneğin, büyük öğe paketlerini indirme, yerel veritabanlarını senkronize etme, ses akışı yapma, egzersiz antrenmanlarını kaydetme veya bağlı cihaz oturumunu sürdürme. Bu arka plan çalışması, ön plan kullanıcı arayüzünüz ve grafik motorunuzla aynı OS işleminde çalıştığında tüm işlem, kullanıcı arayüzünün ayırdığı tüm bellekle birlikte yüksek bir önem düzeyinde (oom_score_adj, 0 ile 250 arasında) kalır.
Bu sayfada, bu durumun uygulamanıza ve cihazın geri kalanına neden zarar verdiği, arka plan işlerinin android:process kullanarak özel bir hafif işleme nasıl taşınacağı ve adb shell ile Perfetto kullanarak bellek tasarrufunun nasıl doğrulanacağı açıklanmaktadır.
Tek işlemli çalışma neden kullanıcı arayüzü belleğini korur?
Android, bellek ve işlem sonlandırmayı tek tek bileşenler (Activity, Service veya BroadcastReceiver) düzeyinde değil, işletim sistemi işlemi (pid) düzeyinde yönetir. Etkinlik Yöneticisi (OomAdjuster), her işleme, o işlemdeki en önemli etkin bileşeni veya gelen bağlamayı yansıtan tek bir oom_score_adj değeri atar:
- Kullanıcı
Activityile etkileşimde bulunduğunda işlemTOPdurumunda (oom_score_adj = 0) çalışır. - Kullanıcı başka bir yere gittiğinde ve uygulamanızda etkin arka plan bileşenleri olmadığında işlem,
PREV(oom_score_adj = 700) veyaCACHED(oom_score_adj >= 900) grubuna taşınır. Önbelleğe alınmış grupta platform, cgroup v2 dondurucusuyla işlemi dondurur,CachedAppOptimizerile belleğini sıkıştırır ve bellek baskısı altındalmkd'nin işlemi önce sonlandırmasına izin verir. - Ancak aynı işlem bir ön plan hizmeti de çalıştırıyorsa (ör.
oom_score_adj = 200adresindekidataSync,mediaPlayback,connectedDeviceveyalocationtürleri) ya da sistemin veya başka bir önemli istemcinin bağlandığı bir hizmeti barındırıyorsaActivitydurdurulduktan sonra bile işlemin tamamı bu yüksek öncelikte kalır.
Yoğun bir kullanıcı arayüzü işlemi oom_score_adj = 200 değerinde kaldığında, belleğin birkaç büyük kategorisi yerleşik olarak kalır:
- Durdurulan
Activityve görünüm hiyerarşisi:Activityaçıkça sonlandırılmadığı sürece, kullanıcı başka bir uygulamadaykenWindow, görünüm ağacı, düzen yapıları ve görünüm durumu Java yığınında erişilebilir durumda kalır. - Yerel grafik bağlamları ve GPU ayırmaları: Standart
HardwareRenderer, GPU bağlamını ve önbelleklerini korurken durdurulanActivity, önbelleğe alınmayan bir işlemde bulunur. Kendi Vulkan (VkDevice,VkSwapchainKHR, tanımlayıcı havuzları, ardışık düzen önbellekleri) veya OpenGL ES (EGLContext, dokular, köşe arabellekleri) bağlamlarını oluşturan uygulamalar ve oyun motorları, motor durdurulduğunda grafik cihazını açıkça kaldırmadığı sürece sürücü yapılarını, gölgelendirici programlarını ve grafik arabelleklerini de korur. - Yığın parçalanması ve çalışma zamanı ek yükü: Bir uygulama
onTrimMemoryiçinde önbellekleri serbest bıraksa bile kısmen doldurulmuş yerel ayırıcı sayfaları, kirli Zygote yazma sırasında kopyalama sayfaları, yüklenen paylaşılan kitaplıklar (.so) ve çalışma zamanı meta verileri işlemin özel belleğinde kalır. İşlem, önbelleğe alınmış eşiğin (oom_score_adj < 900) üzerinde olduğundan platform, işlemi dondurmaz veya önbelleğe alınmış uygulama sıkıştırması çalıştırmaz. Bağlama hizmetlerinde bu etkinin daha yakından incelenmesi için Bellek yoğun kullanıcı arayüzüyle bağlama hizmetini aynı yerde barındırma başlıklı makaleyi inceleyin.
Yalın bir arka plan işlemi, uygulamanızı nasıl korur?
Arka plan durumunda ön plan boyutunda bellek tutmak, kendi arka plan çalışmanızı riske atar:
- Arka plan bellek sınırları, ön plan sınırlarından daha katıdır. Android 17 ve sonraki sürümlerde Bellek Sınırlayıcı, ön plan hizmeti çalışırken de dahil olmak üzere işlemlerinin hiçbiri görünür bir kullanıcı arayüzü göstermediğinde uygulamanıza daha küçük bir görünür olmayan bellek sınırı uygular. Sınır, uygulamanızın tüm işlemlerini birlikte kapsar. Ön plan hizmetinizi çalıştıran işlem, kullanıcı arayüzünü ve grafik belleğini hâlâ tutuyorsa bu bellek daha küçük sınıra dahil edilir. Uygulamanız sınırı aştığında çekirdek, sayfalarını geri alırken uygulamayı kısıtlar. Bu da devam eden indirme, senkronizasyon veya oynatma işlemini yavaşlatır. Uygulamanız, takas alanı dolduktan sonra da bellek ayırmaya devam ederse sistem tarafından sonlandırılır. Uygulamanızın etkilenip etkilenmediğini kontrol etmek için Uygulama bellek sınırları bölümüne bakın.
- Bellek baskısı altında ilk olarak daha büyük işlemler sonlandırılır.
lmkd, bellekte yer açması gerektiğinde ve bir önem grubundaki (ro.lmk.kill_heaviest_task) en ağır işlemi seçecek şekilde yapılandırıldığında, yüzlerce MB kullanıcı arayüzü belleği de tutan bir ön plan hizmeti işlemi, aynı önemdeki daha küçük işlemlerden önce sonlandırılır ve arka plan çalışmanız da bununla birlikte durur. kswapd ve lmkd etkileşimi bölümüne bakın. - Önbelleğe alınan kullanıcı arayüzü durumu dondurulabilir, sıkıştırılabilir ve flash'a geri yazılabilir. Ön plan kullanıcı arayüzü durumunuzun önbelleğe alınmış bir işleme taşınmasına izin vermek, işletim sisteminin işlemi dondurmasına, belleğini zRAM'e sıkıştırmasına ve soğuk sayfalarını tekrar flash depolama alanına yazmasına olanak tanır. Kullanıcı arayüzü durumunuz bozulmadan kalırken yerleşik bellekte kapladığı yer küçülür. Böylece hem arka plan çalışmanız hem de önbelleğe alınmış kullanıcı arayüzünüz, kullanıcı geri dönene kadar hayatta kalma şansını en üst düzeye çıkarır.
- İşlem başına bellek bütçeleri pratik hale gelir. Yalnızca arka plan işlerini çalıştıran bir işlemin küçük ve öngörülebilir bir çalışma kümesi vardır. Bu nedenle, kullanıcı arayüzünüzü kısıtlamadan bu işleme sıkı bir bellek bütçesi verebilirsiniz.
İkinci bir çalışma zamanının küçük bir temel maliyeti olduğundan ikinci bir işlem çalıştırmak, uygulamanızın bellekte kapladığı toplam yeri artırıyor gibi görünebilir. Bu karşılaştırma, yalnızca tek bir işlemin arka plan çalışmasına her geçişinde tüm kullanıcı arayüzünü ve grafik durumunu tamamen serbest bırakabilmesi durumunda geçerlidir. Uygulamalar ve kullanıcı arayüzü araç setleri bunu pratikte güvenilir bir şekilde yapamaz. İşletim sisteminin ayrı bir önbelleğe alınmış kullanıcı arayüzü işlemini dondurmasına, sıkıştırmasına ve geri yazmasına izin vermek, genel durumda çok daha az fiziksel bellek kullanır, uygulamanızı görünür olmayan sınırda tutar ve uygulamanız ile cihaz genelinde düşük bellek nedeniyle sonlandırma işlemlerini azaltır.
Arka plan çalışmalarını özel bir işlemde izole etme
Arka plan çalışması sırasında kullanıcı arayüzü ve grafiklerin bellekte kalmasını önlemek için uzun süreli arka plan bileşenlerini özel bir hafif işlemde çalıştırın, kullanıcı arayüzünüzü ve grafik motorunuzu ana işlemde tutun:
| İşleme | Manifest beyanı | Barındırılan bileşenler | Kullanıcı arka planda çalışma sırasında kullanıcı arayüzünden ayrıldığında durum |
|---|---|---|---|
| Ana kullanıcı arayüzü süreci | Varsayılan (com.example.app) |
Activity, kullanıcı arayüzü görünümleri, resim önbellekleri, Vulkan veya OpenGL ES oluşturucu, ses ve sahne motoru
|
PREV ve ardından CACHED (oom_score_adj >= 900) değerine düşer. Dondurulur, sıkıştırılır ve flash'a geri yazılır.
|
| Arka plan çalışan işlemi | android:process=":bg" |
Ön plan Service, arka plan öğe indiricisi, senkronizasyon çalışanı veya bağlı cihaz yöneticisi
|
Yalnızca çalışanın kendi yığını ve koduyla FGS (oom_score_adj = 200) üzerinde çalışır. Kullanıcı arayüzü görünümleri veya GPU bağlamları yoktur.
|
İşlem ve bellek bütçelerini bildirme
AndroidManifest.xml içinde android:process=":bg" değerini ayarlayarak arka planınızı Service özel bir alt işleme atayın. Baştaki iki nokta üst üste (:) işareti, uygulamanızın UID'si kapsamına giren özel bir işlem oluşturur.
Android 17 QPR2 (API düzeyi 37.2) ve sonraki sürümlerde, işletim sisteminin ön plan kullanıcı arayüzü işleminizi kısıtlamadan arka plan çalışanına sıkı bir çalışma kümesi tavanı uygulaması için işlem bölmeyi <processes> bloğundaki işlem başına uygulama bellek bütçeleriyle birleştirin:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name=".HeavyApplication"
android:label="@string/app_name">
<!-- App-wide budgets that cover every process, including :bg -->
<memory-budget android:maxMb="512" />
<memory-budget
android:maxMb="128"
android:state="background" />
<processes>
<!-- Tight budget for the headless background worker process -->
<process android:process=":bg">
<memory-budget android:maxMb="32" />
</process>
</processes>
<activity
android:name=".MainActivity"
android:exported="true" />
<service
android:name=".AssetDownloadService"
android:process=":bg"
android:foregroundServiceType="dataSync"
android:exported="false" />
</application>
</manifest>
:bg uygulamasındaki bellek kullanımı hem kendi bütçesine hem de uygulama genelindeki bütçeye dahil edilir. Android 17 QPR2'den önceki sürümleri çalıştıran cihazlarda sistem <memory-budget> değerini yoksayar, android:process=":bg" ise tüm API seviyelerinde çalışır.
Application.onCreate'te kapı başlatma
Android, Application sınıfınızı örneklendirir ve paketiniz için oluşturulan her işlemde Application.onCreate yöntemini çağırır. android:process=":bg"'i kullanmaya başlarken sık yapılan bir hata, ağır kullanıcı arayüzü araç setlerini, resim yükleme havuzlarını, Analytics SDK'larını veya yerel motor başlatma (System.loadLibrary) işlemlerini korumasız bir Application.onCreate yönteminin içinde bırakmaktır. Bunu yaptığınızda :bg işlemi, kaçınmaya çalıştığınız aynı temel ayak iziyle şişer.
Android 9 (API düzeyi 28) ve sonraki sürümlerde kullanılabilen Application.getProcessName yöntemini onCreate içinde kontrol edin ve yalnızca mevcut işlemin gerektirdiği bağımlılıkları başlatın:
Kotlin
class HeavyApplication : Application() {
override fun onCreate() {
super.onCreate()
val processName = Application.getProcessName()
if (processName != null && processName.endsWith(BG_PROCESS_SUFFIX)) {
initBackgroundWorkerProcess()
return
}
initMainUiProcess()
}
private fun initBackgroundWorkerProcess() {
// Initialize only lightweight HTTP, storage, and sync primitives.
// Avoid loading native rendering libraries or allocating UI caches.
}
private fun initMainUiProcess() {
// Initialize UI frameworks, image caches, and native engines.
System.loadLibrary("heavy_engine_jni")
}
companion object {
private const val BG_PROCESS_SUFFIX = ":bg"
}
}
Java
public final class HeavyApplication extends Application {
private static final String BG_PROCESS_SUFFIX = ":bg";
@Override
public void onCreate() {
super.onCreate();
String processName = Application.getProcessName();
if (processName != null && processName.endsWith(BG_PROCESS_SUFFIX)) {
initBackgroundWorkerProcess();
return;
}
initMainUiProcess();
}
private void initBackgroundWorkerProcess() {
// Initialize only lightweight HTTP, storage, and sync primitives.
// Avoid loading native rendering libraries or allocating UI caches.
}
private void initMainUiProcess() {
// Initialize UI frameworks, image caches, and native engines.
System.loadLibrary("heavy_engine_jni");
}
}
Jetpack App Startup gibi bir içerik sağlayıcı üzerinden ilk kullanıma hazırlanan kitaplıklar, yalnızca ana işlemde ilk kullanıma hazırlanır. Bunun nedeni, android:process olmadan tanımlanan içerik sağlayıcıların ana işlemde çalışmasıdır. :bg içindeki kod bu tür bir kitaplığa bağlıysa initBackgroundWorkerProcess içinde açıkça ilk kullanıma hazırlayın.
Unity gibi bir oyun motoruyla geliştirme yapıyorsanız arka plan hizmetini, UnityPlayer gibi motor sınıflarına referans vermeyen, Java veya Kotlin ile yazılmış basit bir Android kitaplığında ya da eklentisinde uygulayın. :bg işlemi, motorun yerel kitaplıklarını hiçbir zaman yüklemez. Onaylamak için :bg işlemiyle ilgili adb shell cat /proc/<pid>/maps bölümünde libunity.so veya libil2cpp.so ifadesinin yer almadığından emin olun.
Kullanıcı arayüzü işlemini sabitlemeden iletişim kurma
Kullanıcı :bg içinde AssetDownloadService çalışırken MainActivity uygulamasını açtığında, kullanıcı arayüzünün genellikle canlı indirme ilerleme durumunu göstermesi gerekir. İşlem önemi, istemciden kullandığı hizmeti veya içerik sağlayıcıyı barındıran işleme doğru akar. Bu nedenle, her bağımlılığın yönü önemlidir:
- Çalışana kullanıcı arayüzü bağlama işlemi güvenlidir.
MainActivity,AssetDownloadServiceile bağlandığında sistem,:bgöğesini görünür kullanıcı arayüzü kadar önemli hale getirir. Kullanıcı ayrıldığında kullanıcı arayüzü işlemi önbelleğe alınır ve bağlama artık hiçbir şeyi yükseltmez. - Kullanıcı arayüzü işlemine bağlı olan çalışan güvenli değil.
:bgiçindeki kod bir hizmete bağlanırsa veya ana işlemde çalışan bir içerik sağlayıcıyı sorgularsa sistem, ana işlemi çalışanın yükseltilmiş önemine yükseltir ve kullanıcı arayüzü belleği tekrar sabitlenir. Bunun yerine, kullanıcı arayüzünün çalışanla bir geri çağırma (ör.Messengerveya AIDL geri çağırma arayüzü) kaydetmesini sağlayın. Geri çağırma olarak bir bağlayıcı nesnesi iletmek, her iki işlemin de önemini değiştirmez.
Bağlantıyı kaldırmadan önce geri çağırma kaydını iptal edin. Böylece çalışan, sistem tarafından dondurulduktan sonra kullanıcı arayüzü işlemini hiçbir zaman çağırmaz. Aşağıdaki örnek, yalnızca kullanıcı arayüzü görünür durumdayken çalışanla bağlantı kurar:
Kotlin
class MainActivity : Activity() {
private var downloadService: IBinder? = null
private val downloadConnection = object : ServiceConnection {
override fun onServiceConnected(
name: ComponentName,
service: IBinder
) {
downloadService = service
// Register a Messenger or AIDL callback for progress updates.
}
override fun onServiceDisconnected(name: ComponentName) {
// The binding stays active and reconnects if the worker restarts.
downloadService = null
}
}
override fun onStart() {
super.onStart()
val intent = Intent(this, AssetDownloadService::class.java)
bindService(intent, downloadConnection, Context.BIND_AUTO_CREATE)
}
override fun onStop() {
super.onStop()
if (downloadService != null) {
// Unregister the progress callback here, before unbinding.
downloadService = null
}
// Unbind the ServiceConnection even if onServiceConnected hasn't run
// yet.
unbindService(downloadConnection)
}
}
Java
public final class MainActivity extends Activity {
private IBinder downloadService;
private final ServiceConnection downloadConnection =
new ServiceConnection() {
@Override
public void onServiceConnected(
ComponentName name, IBinder service) {
downloadService = service;
// Register a Messenger or AIDL callback for progress
// updates.
}
@Override
public void onServiceDisconnected(ComponentName name) {
// The binding stays active and reconnects if the worker
// restarts.
downloadService = null;
}
};
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, AssetDownloadService.class);
bindService(intent, downloadConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (downloadService != null) {
// Unregister the progress callback here, before unbinding.
downloadService = null;
}
// Unbind the ServiceConnection even if onServiceConnected hasn't run
// yet.
unbindService(downloadConnection);
}
}
Bağlama işaretlerinin süreçler genelinde oom_score_adj öğesini nasıl etkilediği hakkında daha fazla bilgi için Hizmet bağlamaları ve süreç durumları başlıklı makaleyi inceleyin.
Diskteki paylaşılan durumu güvenli bir şekilde koordine etme
Ana işlem ve :bg, ayrı çalışma zamanları ve dosya tanımlayıcılarla ayrı adres alanlarında çalıştığından:
- SQLite ve Room: Her iki işlemin de veritabanını aynı anda okuyup yazabilmesi için ileriye dönük günlük kaydı (çoğu cihazda Room'daki varsayılan günlük modu) kullanın.
enableMultiInstanceInvalidation,:bgöğesinden çağrılmamalıdır. Room, geçersiz kılma hizmetini ana işlemde barındırır. Bu nedenle çalışan, ana işleme bağlanır ve kullanıcı arayüzü belleğini çalışanın yüksek önem düzeyinde tutar. Bunun yerine, çalışan değişiklikleri kullanıcı arayüzünün kaydettiği geri çağırma işlevi aracılığıyla bildirmeli ve kullanıcı arayüzü başladığında veritabanını yeniden sorgulamalıdır. - Tercihler:
SharedPreferences, süreçler arasında paylaşılmaz. Çok işlemli kodda DataStore kullanın veya durumu IPC aracılığıyla açıkça iletin. - Öğe dosyaları: İndirilen öğe paketlerini
:bgdizinindeki bir hazırlık dosyasına yazın, yazma işleminden sonra:bgdizinindefdatasyncve ardındanposix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED)işlevini çağırın (Bellek sayfalarını işletim sistemine geri döndürme bölümünde gösterildiği gibi) ve dosyayı atomik olarak yeniden adlandırın. Donmuş olabilecek bir kullanıcı arayüzü işlemini çağırmak yerine, kullanıcı arayüzünün bir sonraki başlatılışında sonucu almasına ve çalışana bağlanmasına izin verin.
Etkiyi adb kabuğunda gözlemleme
Ön plan ve arka plan işlemlerinizi ayırmanın, yüksek bellek kullanımını izole ettiğini doğrulamak için ana ekrana gittikten sonra Etkinlik Yöneticisi işlem durumlarını ve dumpsys meminfo değerini inceleyebilirsiniz. Bu bölümdeki örnek çıkış, her iki yapılandırmada da kullanıcı arayüzü işleminde aynı 64 MB yerel ayırmayı çalıştıran ve bölünmüş ön plan hizmetini :remote işleminde barındıran MemoryLab örnek uygulamasından alınmıştır.
1. İşlem durumlarını ve OOM puanlarını karşılaştırma
Arka plan iş yükünü başlatın, ana sayfa düğmesine basarak Activity uygulamasının durmasını sağlayın ve Activity Manager'ı sorgulayın:
adb shell dumpsys activity processes com.example.app | grep -E "Proc #|oom:|state:"
Tek işlemli mimaride, Activity durdurulsa bile ana işlem yükseltilmiş olarak kalır. Kullanıcı ayrıldıktan sonra yaklaşık 15 saniye boyunca Etkinlik Yöneticisi, ön plan hizmeti çalıştıran bir işlemi oom_score_adj = 50 (fg-service-act) seviyesinde tutar ve ardından 200 (fg-service) seviyesine düşürür. Aşağıdaki çıkış, Ana Sayfa'ya basıldıktan 20 saniye sonra yakalanmıştır:
Proc #1: prcp F/S/FGS ---NFUATI t: 0 13794:com.android.memorylab/u0a284 (fg-service)
oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200
state: cur=FGS set=FGS lastRss=0.00 lastCachedRss=0.00
Bölünmüş işlem mimarisinde yalnızca çalışan işlemi oom_score_adj = 200 düzeyinde kalırken ana kullanıcı arayüzü işlemi PREV (oom_score_adj = 700) düzeyine ve daha sonra önbelleğe alınmış gruba (oom_score_adj >= 900) düşer:
Proc #3: prcp F/S/FGS ---NFUATI t: 0 13544:com.android.memorylab:remote/u0a284 (fg-service)
oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200
state: cur=FGS set=FGS lastRss=0.00 lastCachedRss=0.00
Proc #1: prev b/ /LAST -------TI t: 0 13312:com.android.memorylab/u0a284 (previous)
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=0.00 lastCachedRss=0.00
Önceki uygulama zaman aşımı süresi dolduktan sonra ana işlem, önbelleğe alınmış gruba (cch, oom_score_adj = 900, previous-expired) taşınır ve örtük CPU süresi (I) dahil olmak üzere tüm işlem özelliklerini (---------) kaybeder. Bu noktada sistem, işlemi dondurur ve çalışan işlemini kesintiye uğratmadan istediği zaman sıkıştırabilir veya geri alabilir.
2. İşlemler arasında dumpsys meminfo'yu karşılaştırma
Yükseltilmiş çalışanın dumpsys meminfo özetini kullanıcı arayüzü işlemiyle karşılaştırın:
adb shell dumpsys meminfo com.example.app:bg
adb shell dumpsys meminfo com.example.app
oom_score_adj = 200 adresindeki MemoryLab çalışan işlemi için örnek App Summary çıkışı:
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 380 13840
Native Heap: 1548 2832
Code: 132 55816
Stack: 200 208
Graphics: 0 0
Private Other: 496
System: 1926
Unknown: 4168
TOTAL PSS: 4682 TOTAL RSS: 76864 TOTAL SWAP PSS: 441
Private Other içinde 64 MB yerel ayırma içeren oom_score_adj = 700 adresindeki MemoryLab kullanıcı arayüzü işlemi için örnek App Summary çıkışı:
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 1316 19816
Native Heap: 30408 31596
Code: 3148 115796
Stack: 656 664
Graphics: 0 0
Private Other: 67044
System: 13687
Unknown: 71092
TOTAL PSS: 116259 TOTAL RSS: 238964 TOTAL SWAP PSS: 259
Çalışan işlemi, kod ve çalışma zamanı sayfalarının çoğunu Zygote ile paylaştığı için 5 MB'tan az PSS'ye ihtiyaç duyar. Vulkan veya OpenGL ES ile oluşturulan bir uygulamada, kullanıcı arayüzü işleminin Graphics satırı GPU belleğini de içerir.
Bu örnek, yazılım oluşturma özelliğine sahip bir Cuttlefish sanal cihazında yakalandı. Bu nedenle, her iki işlemde de Graphics, 0 olur.
3. İşlem başına cgroup hesaplamasını inceleme
UID'niz altındaki her işlemin, uygulamanızın üst cgroup'unda kendi cgroup v2 dizini vardır. Aşağıdaki komut, uygulamanızın her işlemi için oom_score_adj, memory.current ve uygulama genelindeki memory.high değerlerini listeler:
adb shell '
for PID in $(pidof com.example.app com.example.app:bg); do
CG=/sys/fs/cgroup$(sed -n "s/^0:://p" /proc/$PID/cgroup)
echo "pid=$PID ($(tr -d "\0" < /proc/$PID/cmdline))" \
"adj=$(cat /proc/$PID/oom_score_adj)" \
"memory.current=$(cat $CG/memory.current)" \
"app.memory.high=$(cat ${CG%/*}/memory.high)"
done
'
Ana Sayfa'ya bastıktan sonra MemoryLab için örnek çıkış:
pid=15301 (com.android.memorylab) adj=700 memory.current=103260160 app.memory.high=966787072
pid=16518 (com.android.memorylab:remote) adj=200 memory.current=3608576 app.memory.high=966787072
Çalışan işlem 4 MB'tan az, kullanıcı arayüzü işlemi ise yaklaşık 98 MB ücretlendirilir. Her işlemin kendi memory.current değeri vardır ancak Bellek Sınırlayıcı, sınırını uygulamanızın tüm işlemlerini içeren üst cgroup'ta ayarlar. Her iki satırda da aynı app.memory.high değeri (966.787.072 bayt) gösterilir. Bu değer, aşağıdaki çıkışta görünmeyen 922 MB sınırıdır.
Bellek Sınırlayıcı'nın cihazda uyguladığı görünür ve görünür olmayan sınırları görmek için aşağıdaki komutu çalıştırın:
adb shell am memory-limiter status
4 GB RAM'e sahip bir cihazdan alınan örnek çıkış. Burada görünür olmayan sınır, görünür sınırın yarısıdır:
Memory limiter
enabled limits=true killing=true ignored=none
visible: memHigh=1843MB memMax=2048MB swapMax=1024MB
not-visible: memHigh=922MB memMax=1024MB swapMax=1024MB
Perfetto'da etkiyi gözlemleme
Perfetto, kullanıcı arka planda çalışma devam ederken ön plan kullanıcı arayüzünden ana ekrana geçtiğinde her iki işlemde de oom_score_adj ve RSS zaman çizelgesini görselleştirmenize olanak tanır.
1. İz kaydetme
Uygulamanızda bir arka plan işi başlatırken ve ana ekrana giderken işlem belleği sayaçlarını, GPU belleğini ve oom_score_adj değişikliklerini yakalayan 15 saniyelik bir izleme kaydedin:
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/process-splitting.perfetto-trace <<'EOF'
buffers: {
size_kb: 32768
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.process_stats"
process_stats_config {
scan_all_processes_on_start: true
proc_stats_poll_ms: 250
}
}
}
data_sources: {
config {
name: "android.gpu.memory"
}
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "oom/oom_score_adj_update"
ftrace_events: "gpu_mem/gpu_mem_total"
}
}
}
duration_ms: 15000
EOF
adb pull /data/misc/perfetto-traces/process-splitting.perfetto-trace
2. Perfetto kullanıcı arayüzünde işlem izlerini inceleme
İzlemeyi Perfetto kullanıcı arayüzünde açın ve com.example.app ile com.example.app:bg için işlem gruplarını genişletin:
- Her iki işlemin
oom_score_adjvemem.rss.anonizlerini sabitleyerek zaman çizelgesinin üst kısmında yan yana görünmelerini sağlayın. - Kullanıcının Ana Sayfa düğmesine bastığı zaman damgasını bulun:
- Çalışan işleminde
oom_score_adj,200değerinde kalır vemem.rss.anondüz ve küçük kalır. - Kullanıcı arayüzü işleminde
oom_score_adj,0değerinden200değerine yükselir,Activitydururken yaklaşık bir saniye boyunca bu değerde kalır ve ardından700değerine yükselir (PREV). Önceki uygulama zaman aşımından sonra, sistemin çalışanları etkilemeden belleğini sıkıştırıp geri kazanabileceği önbelleğe alınmış aralığa geçer.
- Çalışan işleminde
Şekil 2'de, kullanıcı arayüzü işlemi kısa bir süre 200 değerinde kalırken Activity değeri durur ve ardından 700 değerine yükselir. Çalışan ise yaklaşık 19 MB anonim RSS ile 200 değerinde kalır. Karşılaştırma için Şekil 3'te, tek işlemin tüm kullanıcı arayüzü belleğiyle 50 ve ardından 200'de kaldığı, aynı konumda bulunan yapılandırmanın 35 saniyelik bir izi gösterilmektedir.
Bu görünümleri keşfetmek için Perfetto kullanıcı arayüzünde bölünmüş izi ve aynı konumdaki izi açın.
3. PerfettoSQL ile yüksek bellekte kaplanan yeri sorgulama
Uygulamanızın her bir işleminin her bir oom_score_adj düzeyinde tuttuğu ortalama anonim ve dosya RSS'sini ölçmek için Sorgu (SQL) sekmesinde aşağıdaki PerfettoSQL sorgusunu çalıştırın:
INCLUDE PERFETTO MODULE android.memory.process;
SELECT
process_name,
score AS oom_score_adj,
ROUND(SUM(dur) / 1e9, 2) AS duration_sec,
ROUND(SUM(anon_rss * dur) / SUM(dur) / 1048576.0, 1) AS avg_anon_mb,
ROUND(SUM(file_rss * dur) / SUM(dur) / 1048576.0, 1) AS avg_file_mb
FROM memory_oom_score_with_rss_and_swap_per_process
WHERE process_name GLOB 'com.example.app*'
GROUP BY process_name, score
ORDER BY process_name, score;
Şekil 2'deki bölünmüş iz için örnek çıkış:
process_name oom_score_adj duration_sec avg_anon_mb avg_file_mb
com.android.memorylab 0 4.11 93.3 110.4
com.android.memorylab 200 1.06 126.8 110.5
com.android.memorylab 700 9.72 115.1 110.5
com.android.memorylab:remote 0 0.01 NULL NULL
com.android.memorylab:remote 200 12.87 18.9 53.7
Şekil 3'teki eş konumlu iz için örnek çıkış:
process_name oom_score_adj duration_sec avg_anon_mb avg_file_mb
com.android.memorylab 0 4.10 96.0 110.4
com.android.memorylab 50 16.04 117.6 110.5
com.android.memorylab 200 14.68 116.5 110.6
Bölünmüş yapılandırmada, ilk saniyeden sonra oom_score_adj = 200 üzerinde yalnızca yaklaşık 19 MB anonim bellek kalır. Aynı konumda bulunan yapılandırmada, ön plan hizmeti çalıştığı sürece yaklaşık 117 MB, 50 ve ardından 200 konumunda kalır.
← Bellek sayfalarını işletim sistemine döndürme | ↑ Yukarı