Android'deki uygulama işlemleri yalıtılmış olarak çalışmaz. Uygulamalar genellikle diğer uygulamaların veya sistemin kendisi tarafından sağlanan hizmetlerden yararlanır. Bir işlem, Hizmet Bağlama aracılığıyla başka bir işleme bağlandığında, Android çerçevesinin belleği yönetme şeklini derinden etkileyen bir bağımlılık oluşturur.
İşlem durumları ve bellek yetersizliği puanları
Android çerçevesi, her çalışan sürecin önemini izlemek için Süreç Durumları'nı kullanır. Bu durumlar daha sonra OomAdjuster tarafından -1.000 ile 1.000 arasında değişen bir OOM puanı ayarlaması (oom_score_adj) değeri atamak için kullanılır.
Daha düşük bir oom_score_adj, işlemin daha önemli olduğu ve Düşük Bellek Sorunu (LMK) tarafından sonlandırılma olasılığının daha düşük olduğu anlamına gelir.
Sık karşılaşılan işlem durumları
Aşağıdaki tabloda, en yaygın işlem durumlarından bazıları ve bunların tipik oom_score_adj değerleri gösterilmektedir. Tam ve güncel liste için Android kaynak kodundaki android.app.ActivityManager ve com.android.server.am.psc.Constants bölümlerine bakın.
| İşlem Durumu (Kısaltma) | Açıklama | Normal oom_score_adj |
|---|---|---|
| PER (Kalıcı) | Her zaman çalışması gereken sistem işlemleri (ör. Telefon). | -800 |
| EN ÇOK | Kullanıcının şu anda etkileşimde bulunduğu işlem. | 0 |
| VIS (Görünür) | İşlemde görünür bir etkinlik vardır (ör. yarı saydam bir iletişim kutusunun arkasında). | 100 |
| PERC (Algılanabilir) | Kullanıcının farkında olduğu arka plan işlemi (ör. müzik çalma) | 200 |
| FGS | Ön plan hizmeti barındıran süreçler | 0-200 (değişir) |
| BTOP (Bound Top) | TOP uygulamasıyla sınırlı işlem. | 100 |
| BFGS | Bağlı Ön Plan Hizmeti (genellikle sisteme bağlıdır). | 0 |
| PREV (Önceki) | Kullanıcının mevcut işlemden önce bulunduğu son işlem. | 700 |
| CACHED | Güvenli bir şekilde sonlandırılabilen arka plan uygulamaları. | 900-999 |
Hizmet bağlamalarının etkisi
Bir istemci işlemi (ör. TOP durumundaki bir uygulama) bir sunucu işlemindeki bir hizmete bağlandığında, sunucu işlemi genellikle daha yüksek bir önceliği devralır. Bu sayede, hizmetin müşteri ihtiyaç duyduğu sürece kullanılabilir kalması sağlanır.

BIND işaretleriyle devralmayı kontrol etme
Context.BIND_AUTO_CREATE kullanılırken varsayılan davranış devralmadır.
Ancak geliştiriciler, bindService() içindeki çeşitli işaretleri kullanarak bağlamanın hedef işlemin önemini nasıl etkileyeceğini kontrol edebilir.
OOM puanı için önemli BIND işaretleri
Sistem genelinde bellek baskısını yönetirken en alakalı işaretler şunlardır:
BIND_AUTO_CREATE: En yaygın işaretleme türü. Bu, hizmet sürecinin bağlama olduğu sürece başlatılmasını ve etkin tutulmasını sağlar. Varsayılan olarak, istemciyle eşleşmesi için sunucu işlem önceliğini de yükseltir.BIND_NOT_FOREGROUND: Hedef hizmetin işleminin ön plana planlama önceliğiyle (CPU önceliği) yükseltilmesini engeller. Ancak bu, bellek önceliğinin (oom_score_adj) yükseltilmesine izin vermeye devam eder. Bu, CPU döngüleri için kullanıcı arayüzüyle rekabet etmemesi ancak yine de sonlandırılmaya karşı korunması gereken arka plan çalışmaları için kullanışlıdır.BIND_WAIVE_PRIORITY: Sisteme hedef işlemin planlama veya bellek yönetimi önceliğini etkilememesi talimatını veren çok güçlü bir işaret. Hizmet süreci, LRU listesindeki normal bir arka plan süreci gibi yönetilir. Bu nedenle, bağlı olsa bile OOM tarafından sonlandırılmaya uygun hale gelir.BIND_ABOVE_CLIENT: Hizmetin, istemci uygulamasından daha önemli olduğunu gösterir. Sistemin belleği geri kazanması gerektiğinde, bağlı hizmeti sonlandırmadan önce istemci uygulamasını sonlandırmayı tercih eder. Bu yöntem, istemci pahasına hizmet için ek bir koruma katmanı sağladığındanBIND_AUTO_CREATEyönteminden "daha güçlüdür".BIND_NOT_PERCEPTIBLE: Hedef hizmetin öneminiPERCEPTIBLEdüzeyinin altına düşürerek sistemin, daha kritik kullanıcı tarafından algılanabilen işlemler için yer açmak üzere belleğini geri kazanmasına olanak tanır.
Uygulamalı: Bağlama efektlerini gözlemleme
TOP uygulamasından gelen bir bağlamanın ayrı bir işlemin durumunu nasıl etkilediğini göstermek için MemoryLab uygulamasını kullanacağız.
1. MemoryLab'i başlatma
Aşağıdaki komut uygulamayı başlatır. Uygulama açıldıktan sonra ön planda kaldığından emin olun (henüz Ana Sayfa düğmesine basmayın veya uygulamalar arasında geçiş yapmayın).
adb shell am start -n com.android.memorylab/.MainActivity
2. Süreçleri belirleme
Bağlama işleminden önce süreç durumlarını kontrol edin. MemoryLab, ana kullanıcı arayüzünü tek bir işlemde çalıştırır ve :remote işleminde çalışan bir RemoteService içerir.
adb shell dumpsys activity processes com.android.memorylab
Örnek çıkış snippet'i:
Process OOM control (154 total, non-act at 7, non-svc at 7):
Proc #0: fg T/A/TOP LCMNFUATI t: 0 13470:com.android.memorylab/u0a417 (top-activity)
oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0
state: cur=TOP set=TOP lastRss=0.00 lastCachedRss=0.00
Ana işlemin com.android.memorylab TOP durumunda olduğunu görürsünüz. :remote işlemi henüz başlatılmadı.
3. Tetikleyici bağlama
Hizmet bağlamayı tetiklemek için uygulamaya bir yayın gönderin:
adb shell am broadcast -a com.android.memorylab.LEAK_BINDER
4. Yükseltilmiş durumu gözlemleme
İşlem durumlarını tekrar kontrol edin:
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
Örnek çıkış snippet'i:
Proc # 1: vis F/ /BTOP ---NFUATI t: 0 13560:com.android.memorylab:remote/u0a417 (service)
com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100
state: cur=BTOP set=BTOP lastRss=0.00 lastCachedRss=0.00
:remote işlemi şu anda çalışıyor ve 100 oom_score_adj ile BTOP (Bound TOP) durumunda. Bu, normal bir arka plan hizmetinden (500 veya daha yüksek) çok daha fazla koruma sağlar. <=Proc{...} notu, bu öncelik yükseltmesinden hangi sürecin sorumlu olduğunu gösterir.
5. Arka plana gönder
Cihazdaki HOME (Ana Sayfa) düğmesine basın. Durumları tekrar kontrol edin:
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
Örnek çıkış snippet'i:
Proc # 2: prev b/ /LAST --------I t: 0 13560:com.android.memorylab:remote/u0a417 (service)
com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=0.00 lastCachedRss=0.00
Proc # 1: prev b/ /LAST --------I t: 0 13470:com.android.memorylab/u0a417 (previous)
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=209MB lastCachedRss=0.00
İstemci işlemi artık TOP olmadığından her iki işlem de daha düşük öncelikli bir duruma (PREV /
oom_score_adj 700) geçti. (Not:
Durum dökümündeki LAST, LAST_ACTIVITY dahili durumu ifade eder ve üst düzey özetlerde PREV ile eşleşir.)
procstats ile analiz etme
procstats aracı, bu durumların geçmiş görünümünü sağlar.
# View stats for MemoryLab over the last hour
adb shell dumpsys procstats --hours 1 com.android.memorylab
Örnek çıkış snippet'i:
* com.android.memorylab / u0a417 / v37:
* Prc com.android.memorylab / u0a417 / v37:
TOTAL: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
Top: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
* Prc com.android.memorylab:remote / u0a417 / v37:
TOTAL: 0.19%
Bnd Top: 0.19%
Burada Bnd Top, uzak işlemin TOP durumundaki bir uygulamaya bağlı olarak geçirdiği sürenin yüzdesini gösterir.
Bağlamaları Perfetto ile yakalama ve analiz etme
dumpsys size anlık bir görüntü sunarken Perfetto, bağlamanın gerçekleştiği anı ve OOM puanının gerçek zamanlı olarak nasıl değiştiğini görmenizi sağlar.
1. İz kaydetme
linux.process_stats ve am atrace kategorisini içeren bir yapılandırma kullanın:
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/service_bindings.perfetto-trace <<EOF
buffers: { size_kb: 65536 }
data_sources: {
config {
name: "linux.process_stats"
process_stats_config { proc_stats_poll_ms: 100 }
}
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config { ftrace_events: "am/am_proc_bound" }
}
}
duration_ms: 15000
EOF
2. Sorgu OOM puanı geçişleri
PerfettoSQL'i kullanarak uzak işlemin OOM puanının kullanıcı arayüzü işlemine göre nasıl değiştiğini görebilirsiniz:
SELECT ts, p.name, value AS oom_score_adj
FROM counter c
JOIN process_counter_track t ON c.track_id = t.id
JOIN process p USING (upid)
WHERE p.name LIKE 'com.android.memorylab%'
AND t.name = 'oom_score_adj'
ORDER BY ts;
3. Bağlama etkinliklerini belirleme
Bağlama bağımlılığının tam olarak ne zaman oluşturulduğunu ve hangi işlemin başlattığını görmek için şu sorguyu kullanın:
SELECT
s.ts,
p.name AS process_name,
t.name AS thread_name,
s.name AS slice_name
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t USING (utid)
JOIN process p USING (upid)
WHERE s.name LIKE 'bindService:{com.android.memorylab%';
Sistemden uygulamaya bağlamalar
Android sisteminin kendisi, temel işlevleri sağlamak için genellikle üçüncü taraf uygulamalardaki hizmetlere bağlanır. Bu bağlamaların amacı genellikle gecikmeyi azaltmaktır. Sistem, bir işlemi canlı ve bellekte tutarak kritik bir kullanıcı etkileşimi gerçekleştiğinde "soğuk başlatma"nın (APK'yı yükleme, çalışma zamanını başlatma ve Application nesnesini oluşturma) maliyetli ek yükünü önler. Arka plan etkinlikleri akışlarını işlemesi gereken uygulamalarda sık sık soğuk başlatma yapılmasını önlemek için başka bağlamalar da vardır.
Tipik bir cihazda gözlemleyebileceğiniz gerçek hayattan bazı örnekleri aşağıda bulabilirsiniz:
VoiceInteractor
Kullanıcılar, dijital asistanın telefonlarının işletim sistemine yerleştirilmesini, konuşulan bir etkinleştirme kelimesi veya hızlı bir giriş hareketiyle anında çağrılabilmesini ve etkileşimin sorunsuz olmasını bekler.
Bir asistan tetikleyici (ör. Google Pixel telefonlarda "Ok Google" etkin kelimesi) gerçekleştiğinde dijital asistanın anında yanıt vermesi gerekir. Bunu sağlamak için system_server, kullanıcının seçtiği sesli etkileşim hizmetine kalıcı bir bağlama oluşturur.

İşlem durumlarını kontrol ederseniz (ör.dumpsys activity processes kullanarak) BFGS (Bound Foreground Service) durumunda com.google.android.googlequicksearchbox:interactor gibi bir işlem görebilirsiniz. Bu işlem, system_server (UID 1000) tarafından yapılan bir bağlama ile canlı tutulur.
NotificationListenerService
Bazı sistemden uygulamaya bağlamalarda amaç gecikmeyi azaltmak değil, sık sık soğuk başlatmayı önlemektir.
NotificationListenerService,
yeni bildirimler yayınlandığında veya kaldırıldığında sistemden gelen çağrıları alan bir hizmettir ve bunun en iyi örneğidir. Tipik bir akıllı telefon kullanıcısı gün boyunca yüzlerce bildirim alabilir. Sistem, bir bildirim dinleyiciden ayrılırsa bu uygulamanın işlemi büyük olasılıkla önbelleğe alınmış duruma geçer ve LMK tarafından sonlandırılabilir.
Birkaç saniye sonra gelen bir sonraki bildirimde sistem, etkinliği iletmek için uygulamanın sürecini baştan başlatmak zorunda kalır. Bu sürekli sonlandırma ve soğuk başlatma döngüsü, işlemi arka planda bağlı ve etkin tutmaktan çok daha fazla CPU ve pil gücü tüketir.
Başlatıcının "-1 ekranı " (haber feed'i)
Modern başlatıcı uygulamaları genellikle temel gezinme işlevini (ana sayfa simgeleri ve widget'lar) başlatıcı ekranlarından birinde kullanılabilen ve başlatıcı kullanıcı deneyimine sorunsuz bir şekilde entegre edilmiş bir haber akışıyla birleştirir. Haber feed'i başka bir uygulama tarafından sağlanabilir. Örneğin, Google Pixel'de Başlatıcı, Google uygulaması tarafından sağlanan bir feed ile entegre olur.
Haber feed'ini görüntülemek için ana ekranınızda sola kaydırdığınızda geçiş sorunsuz olmalıdır. Başlatıcı, haber feed'ini sağlayan uygulamadaki bir hizmet arayüzüne bağlanarak ve bu bağlantıyı başlatıcı etkin olduğu sürece canlı tutarak bunu başarır. Bu sayede, feed içeriği bakmadığınız zamanlarda bile oluşturulmuş ve bellekte hazır tutulur.
Diğer yaygın örnekler
- Başlatıcı (HOME_APP_ADJ): Başlatıcı uygulamasının (Ana Sayfa) öncelik listesinde kendine ait özel bir yeri vardır. Her zaman bir hizmete bağlı olmasa da
HOME_APP_ADJ(genellikle 600) atanır. Kullanıcı sık sık geri döndüğü için sistem, başlatıcıyı etkin tutmayı tercih eder. Aslında sistem, başlatıcıyı kapatmak yerine daha önce kullanılan uygulamayı (PREV_APP_ADJ = 700) kapatmayı tercih eder. Bunun nedeni, başlatıcının kapatılmasının herhangi bir uygulamadan çıkarken kullanıcı deneyiminin yavaşlamasına neden olmasıdır. Kullanıcının, başlatıcının baştan başlatılmasını beklemesi gerekir. - Giriş yöntemi düzenleyici (IME): Yazarken sistem, seçtiğiniz klavye uygulamasına (ör. Gboard) bağlanır. Bu, klavye geçici olarak gizlense bile klavye işleminin yükseltilmiş durumda kalmasını sağlar. Bu sayede, başka bir metin alanına dokunduğunuzda klavye anında yeniden görünür.
- NFC ile Ödemeler: Ödeme yapmak için telefonunuza dokunduğunuzda sistem, NFC ile ödeme hizmetine (ör. Google Cüzdan) bağlanır. Bu işlemler genellikle satıcı terminalinden katı anlık işlem koşulları gerektirir. Ödeme uygulaması baştan başlatma yapmak zorunda kalırsa işlemin zaman aşımına uğrayıp başarısız olması mümkündür.
Tavizler ve performans düşüşü
Bağlamalar performans ve doğruluk için gerekli olsa da sistemin bellek sağlığı açısından maliyetlidir.
- Esnekliğin Azalması: Bağlı her işlem, LMK'nin kolayca sonlandıramadığı bir işlemdir. Bu, sistemin baskı altında belleği boşaltmak için kullanabileceği önbelleğe alınmış işlemlerin "yastığını" azaltır.
- Performans düşüşünü kötüleştirme: Çok fazla işlem bağlıysa sistem, sonlandırılabilir arka plan işlemleri neredeyse hiç kalmamış bir durumda olabilir. Bellek baskısı arttığında sistem, daha önemli işlemleri sonlandırmaya veya sayfa önbelleğini boşaltmaya zorlandığı için "performans uçurumundan" çok daha hızlı düşer.
Yaygın hizmet bağlama anti-pattern'leri
Hizmet bağlamaları doğrudan oom_score_adj yükselttiğinden, bağlamaları edinme veya yapılandırma şeklinizdeki küçük yaşam döngüsü hataları, büyük miktarda belleğin saatlerce ayrıcalıklı durumlarda (BTOP, BFGS veya PERC) kalmasına neden olabilir. Bağlı hizmetleri tasarlarken veya denetlerken bu yaygın anti-pattern'lere dikkat edin.
Uzun süredir devam eden ilişkilerde unutulan unbindService()
Application tekil öğesinden, arka plan yöneticisinden veya onStart() içinde bindService() işlevini eşleşen bir unbindService() olmadan onStop() içinde çağıran bir Activity öğesinden bir hizmete bağlama işlemi ServiceConnection öğesinin sızmasına neden olur.
Bu bağlama etkin kaldığı sürece hedef işlem, istemcinin yükseltilmiş önceliğini devralır. İstemci kalıcı bir sistem bileşeni veya ön planda çalışan uygulama ise bağlama hizmeti süreci BFGS veya BTOP'de süresiz olarak sabitlenir (procstats'de% 100'e yakın görünür). Bu durum, hizmet tamamen boşta olsa bile LMK'nin belleğini geri almasını engeller.
Çözüm: Kapsam bağlamalarını, bunları gerektiren bileşenin yaşam döngüsüyle sınırlayın veya bir süre işlem yapılmadığında unbindService() işlevini çağıran bir boşta kalma zaman aşımı uygulayın. Her bir RPC çağrısında bağlantıyı çözüp yeniden bağlamaktan kaçının. Bu, işlem aşırı bellek kullanımına ve tekrarlanan binder kurulumu ek yüküne neden olur. Bunun yerine, kısa bir boşta kalma zamanlayıcısının (örneğin, 5 ila 30 saniye) arkasında iş patlamalarını birleştirin.
Bağlı bir hizmeti, bellek yoğun bir kullanıcı arayüzüyle birlikte konumlandırma
Varsayılan olarak, bir APK'daki tüm bileşenler aynı süreçte çalışır. Uygulamanız, ana Activity ile aynı süreçte hafif bir bağlı hizmet (ör. NotificationListenerService, widget sağlayıcı veya sistemin ya da başlatıcının bağlandığı bir eklenti hizmeti) kullanıyorsa sürecin tamamı hizmetin yükseltilmiş durumunu (BFGS veya PERC, genellikle 200 ya da daha düşük oom_score_adj) devralır.
Kullanıcı, uygulamanızın kullanıcı arayüzünü açtığında işlem, büyük görünüm hiyerarşileri, kodu çözülmüş bit eşlemler ve grafik arabellekleri ayırır. Kullanıcı başka bir yere gittiğinde, etkin hizmet bağlaması işlemi yükseltilmiş durumda tuttuğu için işlem CACHED'ya (900 veya daha yüksek oom_score_adj) düşmez. Bu durum, iki katmanlı soruna neden olur:
- Arka planda bellek sıkıştırma yok: Sistem
CachedAppOptimizer, işlemleri yalnızcaCACHEDdurumuna girdikten sonra sıkıştırır. - Önbelleğe alınmış LRU bellek kırpma veya LMK geri kazanımı yok: Bir hizmet bağlaması işlemi yüksek bir durumda tutarken sistem, önbelleğe alınmış LRU listesiyle ilişkili arka plan kırpma geri aramalarını (
TRIM_MEMORY_BACKGROUNDve üzeri) sunmaz. UygulamanızTRIM_MEMORY_UI_HIDDENveyaActivity.onStop()üzerinde kullanıcı arayüzü kaynaklarını açıkça serbest bırakmıyorsa en yüksek kullanıcı arayüzü ayırmaları, LMK'nın kolayca geri alamayacağı ayrıcalıklı bir OOM puanında RAM'e sabitlenmiş olarak kalır.
Çözüm: Kullanıcı arayüzünüz görünür olmaktan çıktığında Activity.onStop(), ComponentCallbacks2.onTrimMemory(TRIM_MEMORY_UI_HIDDEN), Application.ActivityLifecycleCallbacks veya ProcessLifecycleOwner kullanarak kullanıcı arayüzü önbelleklerini, çözümlenmiş bit eşlemleri ve görünüm referanslarını açıkça temizleyin. Alternatif olarak, android:process manifest özelliğini kullanarak her zaman bağlı hizmeti ayrı ve hafif bir işleme taşıyın. Böylece sistem, ana kullanıcı arayüzü işleminizi belleğini bağımsız olarak sıkıştırmak veya geri kazanmak için CACHED durumuna taşıyabilir.
Öncelik feragat işaretlerini atlama
Yalnızca BIND_AUTO_CREATE ile bindService()'ı çağırmak, arayanın tüm planlama ve bellek önceliğini hedef hizmete aktarır. Bir ön plan uygulaması yalnızca BIND_AUTO_CREATE kullanarak arka planda çalışan bir analiz, günlük kaydı veya önceden getirme hizmetine bağlandığında, arka planda çalışan bu hizmeti istemeden BTOP'ye yükseltir.
Çözüm: Ön plan kullanıcı arayüzüyle aynı koruma düzeyine ihtiyaç duymayan yardımcı veya en iyi çaba hizmetlerine bağlanırken BIND_AUTO_CREATE ile BIND_WAIVE_PRIORITY, BIND_NOT_FOREGROUND veya BIND_NOT_PERCEPTIBLE öğelerini birleştirin. Böylece sistem, hedef işlemi önbelleğe alınmış LRU listesinde yönetmeye devam edebilir.
← Yerel | ↑ Yukarı | Sistem genelinde →