Bit eşlem nesneleri, genellikle bir uygulamanın bellekte kaplanan yerine en büyük katkıyı sağlayan tekil öğelerdir. Uygulama simgeleri, bildirim resimleri veya medya içerikleri olsun, verimsiz bit eşlem işleme, hızlı bir şekilde bellek yetersizliği (OOM) hatalarına ve sistem genelinde bellek baskısına yol açabilir.
Bit eşlem yapılandırmaları ve piksel verileri
Bit eşlemin kullandığı bellek miktarı öncelikle boyutlarına (genişlik × yükseklik) ve yapılandırmasına (Bitmap.Config) göre belirlenir.
Yapılandırma, her pikseli temsil etmek için kaç bayt kullanıldığını tanımlar:
| Yapılandırma | Piksel Başına Bayt Sayısı | Açıklama |
|---|---|---|
ALPHA_8 |
1 | Yalnızca alfa (şeffaflık) kanalı. Maskeler için kullanışlıdır. |
RGB_565 |
2 | Kırmızı (5 bit), Yeşil (6 bit), Mavi (5 bit). Alfa sürümü yok. Yüksek renk doğruluğunun önemli olmadığı opak görüntüler için uygundur. |
ARGB_8888 |
4 | Alfa, kırmızı, yeşil, mavi (her biri 8 bit). Varsayılan ve en yaygın olanıdır. |
RGBA_F16 |
8 | Yarım duyarlıklı kayan nokta. Geniş gamlı ve HDR içeriklerde kullanılır. |
HARDWARE |
Yok | Ekran kartı belleğinde (gralloc/DMABuf) saklanır. Donanım Bit Eşlemleri bölümüne bakın. |
Bellek Formülü: Memory (Bytes) = Width × Height × Bytes Per Pixel
Örneğin, 1080p cihazda (1920x1080) tam ekran bir resim ARGB_8888
boyutundadır: 1920 × 1080 × 4 bayt ≈ 8,3 MB.
Yığın bit eşlemleri ve paylaşılan bit eşlemler
Yığın bit eşlemleri (yerel yığın)
Modern Android'de (8.0 ve sonraki sürümler) bit eşlem piksel verileri Native Heap'te depolanırken Java yığınında yalnızca küçük bir sarmalayıcı nesne bulunur.
Bir uygulamanın resim göstermesi gerektiğinde, resim genellikle sıkıştırılmış bir resim dosyasından Bitmap'e çözülür ve yığında saklanır.
Paylaşılan bit eşlemler (ashmem/memfd)
Bir bit eşlem işlemler arasında aktarıldığında (ör. bildirim için Binder aracılığıyla SystemUI'ye) Android, paylaşılan bellek (ashmem veya memfd) kullanarak piksel verilerini kopyalamaktan kaçınır.
Bir Bitmap örneği, Bitmap.asShared() çağrılarak paylaşılan belleğe açıkça veya bir Bitmap, Parcel içine yerleştirilirse (genellikle bit eşlemi Bundle gibi bir Parcelable'ye ekleyerek) ve binder IPC üzerinden gönderilirse örtülü olarak kopyalanabilir.
Binder IPC üzerinden paylaşılan bir Bitmap gönderildiğinde piksel verileri kopyalanmaz. Bunun yerine, paylaşılan bir bellek bölgesine referans veren bir dosya tanımlayıcısı alıcı işleme kopyalanır. Temel bellek bölgesi birden fazla işlem arasında paylaşılabilir ve kendisine referans veren tüm dosya tanımlayıcıları kapatılana kadar boşaltılmaz.
Değişebilir ve değişmez bit eşlemler
- Değiştirilebilir Bit Eşlemler: Oluşturulduktan sonra değiştirilebilir (ör.
Canvasaracılığıyla). Her zaman kendi özel bellek ayırmalarını gerektirirler. Değiştirilebilir bir Bitmap kopyalanırsa derin kopya (tüm piksel verilerinin ikinci kopyası) oluşturulmalıdır. - Değiştirilemez Bit Eşlemler: Değiştirilemez. Bu sayede, farklı
Bitmapörnekleri arasında aynı temel bellek arabelleğini paylaşma gibi optimizasyonlar yapılabilir. APK kaynaklarından (BitmapFactory) yüklenen bit eşlemler genellikle sabittir.
Verimli bit eşlem işleme
Bit eşlem havuzu ve yeniden kullanımı
Bit eşlemlerin sık sık ayrılması ve serbest bırakılması ayrılma karmaşasına neden olur. Bu durum, çöp toplama işleminin sürekli olarak çalışmasına yol açar. Yaygın olarak kullanılan resim yükleme kitaplıkları Bitmap Pool kullanır.
Google, Java tabanlı uygulamalar için Glide'ı, Kotlin tabanlı uygulamalar için ise Coil'i (özellikle Jetpack Compose kullanılırken) çözüm olarak önerir.
Artık ihtiyaç duyulmayan bir bit eşlem, çöp toplama işlemine tabi tutulmak yerine uygulama tarafından bitmap.recycle() çağrılarak veya bir havuza döndürülerek serbest bırakılır. Aynı boyut ve yapılandırmaya sahip bir bit eşlem bir sonraki sefer gerektiğinde havuz, yeni bir ayırmayı önleyerek mevcut arabelleği sağlar.
Donanım bit eşlemleri
Bitmap.Config.HARDWARE, piksel verilerini doğrudan grafik belleğinde (DMABuf) depolamanıza olanak tanır.
- Artıları:
- Bellek tasarrufu: Uygulama veya yerel yığını kullanmaz, GPU belleğini kullanır. Genellikle bir uygulamanın kullanıcı arayüzünde gösterilen bit eşlemlerin GPU belleğine kopyalanması gerekir. Bu nedenle, bu işlem kopyalama işlemini ve eklenen bellek maliyetini ortadan kaldırır.
- Performans: Veriler GPU'da olduğu için çizim son derece hızlıdır.
- Eksileri:
- Değiştirilemez: Donanım bit eşlemleri değiştirilemez.
- Geri okuma yavaş: CPU'dan piksellere erişmek (ör.
getPixel()) çok maliyetlidir. - İlişkilendirme: AHAT gibi standart araçlarda izlenmesi daha zordur (aşağıya bakın).
Bit eşlem belleğiyle ilgili sık karşılaşılan sorunlar
Modern bit eşlem yapılandırmalarını kullandığınızda bile, bit eşlemlerin kodunu çözme ve planlama şeklinizdeki yinelenen birkaç kalıp, büyük bellek artışlarına neden olabilir.
Aşırı ölçeklendirilmiş bit eşlemlerin kodu çözülüyor
Tam çözünürlüklü 4.000 × 3.000 piksellik bir fotoğraf ARGB_8888'da 48 MB yer kaplar. Tam görüntünün yalnızca 200 × 150 piksellik bir küçük resimde oluşturulması için kodunun çözülmesi, ayrılan piksel arabelleğinin% 99'undan fazlasının boşa harcanmasına neden olur.
Resimlerin kodunu doğrudan ImageDecoder veya BitmapFactory ile çözerken ImageDecoder.setTargetSize() ya da BitmapFactory.Options.inSampleSize kullanarak hedef görünüm boyutlarıyla eşleşecek şekilde kod çözme geçişi sırasında alt örnekleme yapın. Glide ve Coil gibi resim yükleme kitaplıkları, sınırlı bir hedef görünüm boyutu sağladığınızda bu alt örnekleme işlemini otomatik olarak gerçekleştirir.
Örneğin, ImageDecoder ile bir bit eşlemi çözerken çıkış boyutlarını hedef görünüm boyutunuza göre ölçeklendiren bir OnHeaderDecodedListener iletin:
val source = ImageDecoder.createSource(resources, R.drawable.high_res_photo)
val bitmap = ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
if (info.size.width > targetWidth || info.size.height > targetHeight) {
decoder.setTargetSize(targetWidth, targetHeight)
}
}
Paralel kod çözme sayesinde yüksek eşzamanlı elde tutma oranı
Tek tek bit eşlemler uygun boyutta ve kısa ömürlü olsa bile birçok görüntünün paralel olarak kodunun çözülmesi ciddi bellek artışlarına neden olabilir. Örneğin, bir düzenleyici veya galeri ekranı, simgeleri ya da küçük resimleri aynı anda kod çözmek için sınırsız bir iş parçacığı havuzunda 30 görev gönderirse sıkıştırılmamış 30 piksel arabelleği ve kod çözücü geçici arabellekleri aynı anda RAM'i kaplar.
Bu yüksek eşzamanlı elde tutma oranı, en yüksek yerel yığın ayak izini artırır ve toplu işlem tamamlanmadan önce lmkd sonlandırmalarını tetikleyebilir. Dispatchers.IO.limitedParallelism(2) gibi sınırlı bir iş parçacığı havuzu, semafor veya eş yordam dağıtıcı ile eşzamanlı kod çözme işleminizi sınırlayın. Böylece, aynı anda yalnızca birkaç bit eşlem kod çözme işlemi gerçekleştirilir.
Kare işleme döngülerinde geri dönüştürülmemiş geçici bit eşlemler
Android 8.0 ve sonraki sürümlerde Java Bitmap sarmalayıcı nesnesi, Java yığınında yalnızca yaklaşık 56 bayt yer kaplarken piksel arabelleği yerel yığında bulunur ve birkaç megabayt yer kaplayabilir. Bu ayrımı Android Studio Memory Profiler veya AHAT'ta doğrulayabilirsiniz. Burada her Bitmap örneği, çok megabaytlık yerel boyutuyla birlikte yaklaşık 56 baytlık sığ Java boyutu gösterir. Ayrıca, dumpsys meminfo'ta Native Allocations (Bitmap (malloced)) altında da doğrulayabilirsiniz.
Kamera çerçeve analizi, OCR veya makine öğrenimi çıkarımı döngüleri gibi yüksek sıklıklı işlem hatları, döndürme veya kırpma için ImageProxy.toBitmap() ve Bitmap.createBitmap() çağrılarını yaparak her karede yeni bir bit eşlem ayırır.
Eski çerçevelere yapılan referansları geri dönüştürmeden bırakmak yerel belleğin şişmesine neden olabilir. Küçük Java sarmalayıcıları, Java yığın kullanımını neredeyse hiç artırmaz. Bu nedenle, NativeAllocationRegistry geri almadan önce yüzlerce megabayt yerel piksel arabelleğinin birikmesini önlemek için atık toplama işlemini yeterince hızlı tetiklemezler.
Kareleri sıkı bir döngüde işlerken mümkünse önceden ayrılmış arabellekleri yeniden kullanın veya her kare işleme tamamlandığında geçici ara bit eşlemlerde bitmap.recycle() işlevini açıkça çağırın.
Uygulamalı alıştırma: bit eşlem keşfi
Bu kavramları incelemek için BitmapLab örnek uygulamasını kullanacağız.
1. dumpsys meminfo ile ölçüm yapma
BitmapLab'i başlatın ve ALLOCATE 10MB ARGB_8888'e dokunun. Ardından şunu çalıştırın:
adb shell dumpsys meminfo -s com.android.bitmaplab
Modern Android sürümlerinde Native Allocations (Yerel Tahsisler) bölümünü bulun. Bunlar, bit eşlemler için genel Uygulama Özeti'nden çok daha iyi ilişkilendirme sağlar:
Native Allocations
Count Total(kB)
------ ------
Bitmap (malloced): 1 10240 # <--- 10MB Bitmap data!
Bitmap (nonmalloced): 0 0
- Bit eşlem (malloced): İşlemin yerel yığınında ayrılan bit eşlemler. Bu, Android 8.0 ve sonraki sürümlerde çoğu standart bit eşlemin bulunduğu yerdir.
- Bit eşlem (malloc'siz): Donanım bit eşlemleri veya paylaşılan bit eşlemler (
ashmemveyamemfdaracılığıyla) gibi özel bellek kullanan bit eşlemler.
BitmapLab'de Paylaşılan Bit Eşlem ayırırsanız bu, Bitmap (nonmalloced)'ye yansıtılır:
Native Allocations
Count Total(kB)
------ ------
Bitmap (malloced): 1 10240
Bitmap (nonmalloced): 1 10240 # <--- Shared Bitmap!
Paylaşılan bit eşlemlerin izlenmesi
Bazı Android sürümlerinde ve çekirdek yapılandırmalarında dumpsys meminfo, dosya tanımlayıcıları aracılığıyla işlemin adres alanına eşlenen bit eşlemler için yüksek çözünürlüklü izleme de sağlar.
Paylaşılan bit eşlemler varsayılan olarak genel bir ad ("bit eşlem") kullanır. Ayrıntılı ilişkilendirme ve benzersiz bit eşlem izlemeyi (paylaşılan bit eşlemleri farklı işlemler arasında tanımlama) etkinleştirmek için aşağıdaki sistem özelliğini etkinleştirmeniz gerekir:
adb shell setprop debug.hwui.bitmap_ashmem_long_name true
Etkinleştirildiğinde, /proc/<pid>/smaps içindeki ashmem bölgelerinin adları daha açıklayıcı olur. meminfo bu durumdan yararlanır ve sonuçlar şu şekilde görünür:
Shared Bitmaps
Count Size(KB)
------ ------
Mapped: 1 10240
Unique: 1 10240
- Eşlenmiş: Bit eşlemle ilgili tüm bellek eşlemelerinin toplam boyutu.
- Benzersiz: Yalnızca benzersiz öğeler dikkate alınarak hesaplanan bit eşlemlerin boyutu (ör. aynı temel paylaşılan bit eşlem piksel verilerinin iki veya daha fazla eşlemesi yalnızca bir kez sayılır).
2. AHAT'deki bit eşlemler
AHAT, Bitmaps için mükemmel görselleştirme sağlar.
- BitmapLab'da birkaç bit eşlem ayırın.
-bişaretini kullanarak yığın dökümü alın (yerel bit eşlem verilerini dahil etmek için):adb shell am dumpheap -b png com.android.bitmaplab /data/local/tmp/bitmaps.hprof adb pull /data/local/tmp/bitmaps.hprof . ahat bitmaps.hproflocalhost:7100simgesini açın ve kenar çubuğunda Bitmaps bağlantısını bulun veyaBitmapsınıfını arayın.AHAT, bit eşlemleri tarayıcıda oluşturarak hangi resimlerin belleği çok kullandığını belirlemeyi kolaylaştırır.

3. Perfetto'da bit eşlem parçaları
Perfetto, bit eşlem ayırmalarını ve sayılarını zaman içinde izleyebilir. Bu sayaçlar, belirli bir uygulama için gfx atrace kategorisi etkinleştirildiğinde Android çerçevesi tarafından yayınlanır.
İzleme başlatın.
gfxkategorisini eklemeniz ve-aişaretini kullanarak belirli bir uygulama paketini hedeflemeniz gerekir:external/perfetto/tools/record_android_trace -o bitmaps.perfetto-trace \ -t 15s -b 64mb view gfx dalvik am res memory -a com.android.bitmaplabBitmapLab'de Allocate (Ayır) ve Clear (Temizle) düğmelerine art arda dokunun.
Ayrıca Parcel/Unparcel Bitmap'e (Paketle/Paketi Aç Bitmap) dokunun.
İzi ui.perfetto.dev'de analiz edin.
com.android.bitmaplab için işlem bölümünde şunları görürsünüz:
* Bitmap Sayısı: Etkin bit eşlemlerin sayısını gösteren bir sayaç.
* Bit eşlem belleği: Bit eşlemler tarafından kullanılan toplam bayt sayısını gösteren sayaç.
Üst düzey dilimler (Perfetto SDK)
BitmapLab, bit eşlem işlemleri için üst düzey dilimler yayınlamak üzere Perfetto SDK'sını da kullanır. İzde BitmapLab_ ifadesini arayarak şunları bulabilirsiniz:
* BitmapLab_parcelUnparcel: Parsel oluşturma ve parsel kaldırma mantığını kapsayan dilimler.
* BitmapLab_postNotification: Bildirim yayınlama akışını kapsayan dilimler.
Bildirim akışlarını izleme
Bildirim Yayınla'ya dokunduğunuzda uygulama, mevcut bit eşlemi içeren bir bildirim oluşturur ve bunu sisteme gönderir. Bundan sorumlu çerçeve kodu, paketleme (bit eşlemi Binder IPC üzerinden gönderilecek bir pakete yazma) ve paket açma (bit eşlemi alıcı taraftaki bir paketten okuma) işlemlerini bağlayan akış etkinlikleriyle Perfetto dilimleri yayar.
Aşağıdaki ekran görüntüsünde, bildirimi yayınlamak için Binder işleminde kullanılacak büyük bit eşlemi parçalayan uygulama ve system_server sürecindeki ilgili parçalama kaldırma işlemi gösterilmektedir.

Perfetto'yu kullanarak, aynı bildirim bit eşlemini iş parçacıkları ve işlemler arasında daha da yayılırken takip edebilirsiniz. Örneğin, system_server'daki (INotificationManager Binder sunucusunu uygulayan) bir bağlayıcı iş parçacığından, aynı bit eşlemi bildirim gölgesinde gösterilmek üzere com.android.systemui'ye iletebilecek system_server çalışan iş parçacıklarına kadar.
Sistem uygulamasıyla ilgili sorunlar
SystemUI (Bildirimler) ve Launcher gibi sistem uygulamaları benzersiz zorluklarla karşı karşıyadır:
- Sınırsız İçerik: Bildirimler ve widget'lar çok sayıda olabilir. Her biri büyük bir bit eşlem içeriyorsa sistemin belleği kısa sürede tükenebilir.
- Yineleme: Aynı uygulama simgesi, Başlatıcı'nın önbelleğinde, SystemUI'nın bildirim alanında ve Ayarlar uygulamasında tutuluyor olabilir.
- Donanım arabellekleri üzerinden paylaşım: Bunu azaltmak için sistem bileşenleri, işlemler arasında
HardwareBufferörneklerini paylaşan merkezi bir "görüntü boşaltma" hizmetine geçiş yapıyor. DMABuf ilişkilendirmesi: Donanım bit eşlemleri yığın alanından tasarruf sağlar ancak standart bellek araçlarında belirli bir süreçle ilişkilendirilmesi daha zor olan DMABuf belleğini kullanır.
Sistem genelindeki DMABuf ayırmalarını görmek için
adb shell dmabuf_dumpsimgesini kullanın. Bu araç, arabelleklerin süreç bazında dökümünü sağlar:droid.bitmaplab:19562 Name Rss Pss nr_procs Inode Exporter <unknown> 3840 kB 1280 kB 3 3397 virtio_gpu system 12 kB 4 kB 3 3398 system <unknown> 3840 kB 1920 kB 2 3399 virtio_gpu system 12 kB 6 kB 2 3400 system PROCESS TOTAL 11556 kB 5136 kB- RSS: İşlemde eşlenmişse arabelleğin toplam boyutu.
- Pss: Orantılı boyut (RSS'nin arabelleği paylaşan işlem sayısına bölünmesi). Bu, muhasebe için en iyi metriktir.
- nr_procs: Şu anda bu arabelleğe referans tutan işlemlerin sayısı.
- Dışa aktarıcı: Arabelleği ayıran sürücü (ör.
virtio_gpuCuttlefish'te veya donanımdaki tedarikçiye özel bir Ion/DMA-BUF yığını).
Tüm arabelleklerin ve sistem genelinde toplam DMA-BUF kullanımının özetini görmek için
adb shell dmabuf_dump -bkomutunu da kullanabilirsiniz.