Java belleğini analiz etme

Java ve Kotlin uygulamaları, çöp toplayıcı tarafından toplanan bir yığın aracılığıyla belleği yönetir. Nesnelere artık ulaşılamadığında çöp toplayıcı (GC) sonunda alanlarını geri alır. Bellek sızıntıları, artık ihtiyaç duyulmayan nesneler "GC kökleri" tarafından tutulmaya devam ettiğinde ve bu nedenle geri alınamadığında meydana gelir.

Temel kavramlar

GC kökleri

GC kökü, çöp toplayıcının her zaman erişilebilir olarak değerlendirdiği özel bir nesne türüdür. Örnekler:

  • Etkin iş parçacıkları (ve şu anda yürütülen Java yığın çerçevelerinden referans verilen nesneler).
  • Etkin olarak çalışan yöntemlere sahip sınıflar.
  • JNI referansları (yerel kod tarafından tutulan genel veya yerel referanslar).

GC köküne giden yol

Bir GC kökünden bir nesneye referans zinciri olduğu sürece bu nesne "erişilebilir" durumdadır ve çöp toplama işlemine tabi tutulamaz. Bu zincire GC köküne giden yol denir. Bellek sızıntısını düzeltmek için bu zinciri belirleyip kırmanız gerekir.

GC kökünün yolu

Dominator ağaçları

GC köküne giden yol, bir nesnenin neden canlı olduğunu gösterse de bu referans bozulursa ne kadar bellek geri kazanılacağını göstermez. Bunun için Dominator Trees'i kullanırız.

Herhangi bir GC kökünden B'ye giden her yol A'dan geçmek zorundaysa A nesnesinin B nesnesine hakim olduğu söylenir. A, B'ye hakimse A'nın geri alınması, B'nin de geri alınabileceğini garanti eder. Bunun nedeni, herhangi bir kökten B'ye giden başka yol olmamasıdır.

Aşağıdaki şemada bir nesne grafiği ve buna karşılık gelen baskın öğe ağacı gösterilmektedir. Grafikte D nesnesine hem A hem de B tarafından ulaşıldığını, dolayısıyla A veya B'nin D üzerinde baskın olmadığını, bunun yerine GC Root'un en yakın baskın olduğunu unutmayın.

Dominator Tree

Java yığın dökümlerini alma

Yığın dökümü, belirli bir anda Java yığınındaki tüm nesnelerin anlık görüntüsüdür.

ADB'yi kullanma

Çalışan bir işlemden yığın dökümü almak için paket adını doğrudan am dumpheap'ya iletebilirsiniz. Bu komutu çalıştırmak için uygulamanızı <profileable android:shell="true"/> veya <debuggable> ile oluşturmanız gerekir.

# 1. Trigger the dump (the command takes a moment to complete):
adb shell am dumpheap -g -b png com.android.memorylab /data/local/tmp/heap.hprof

# 2. Pull the file to your development machine:
adb pull /data/local/tmp/heap.hprof .

Perfetto'yu kullanma

Perfetto, Perfetto yapılandırmanızda android.java_hprof veri kaynağını etkinleştirerek sistem genelinde izlemenin bir parçası olarak Java yığın dökümlerini de yakalayabilir. Bu, yığın durumunu diğer sistem etkinlikleriyle ilişkilendirmek için kullanışlıdır.

Perfetto'yu kullanarak MemoryLab uygulaması için yığın dökümü almak üzere aşağıdaki komutu kullanabilirsiniz:

# Create a temp file for the configuration
cat > /tmp/java_heap.pbtx <<EOF
data_sources: {
    config {
        name: "android.java_hprof"
        java_hprof_config {
            process_cmdline: "com.android.memorylab"
        }
    }
}
EOF

# Run trace command referencing the file
external/perfetto/tools/record_android_trace -o java_heap.perfetto-trace \
  -t 10s -c /tmp/java_heap.pbtx

Perfetto'daki Java yığın dökümleri başlıklı makaleyi inceleyin.

AHAT ile analiz etme

AHAT (Android Heap Analysis Tool), .hprof dosyalarını web tarayıcısında görüntülemek için önerilen araçtır.

AHAT'ı başlatma

Yolunuza ahat yüklediyseniz şu komutla başlatın:

ahat heap.hprof

Alternatif olarak, bağımsız JAR'ı çalıştırın:

java -jar ahat.jar heap.hprof

Ardından tarayıcınızda http://localhost:7100 adresini açın.

AHAT'ı edinme veya oluşturma hakkında ayrıntılı bilgi için AHAT kaynak deposuna bakın.

Temel analiz iş akışları

Sızıntıları bulma

Ayrılanlar görünümünde Etkinlik sınıfınızı (MainActivity) bulun.

Örnekleri gösteren AHAT görünümü

Tüm örnekleri bulmak için sınıfı tıklayın.

MainActivity örneklerini gösteren AHAT görünümü İncelemek için MainActivity örneğini tıklayın.

Örnek ayrıntılarını gösteren AHAT

Örnek görünümünde, nesnenin çöp toplama işlemine tabi tutulmasını engelleyen referans zincirini gösteren GC kökünden örnek yol ve bu belirli örnek tarafından ne kadar bellek tutulduğunu gösteren Nesne boyutu'nu bulabilirsiniz.

GC kökünden ve nesne boyutundan AHAT örnek yolu

Bit eşlemlerini analiz etme

AHAT, genellikle büyük bellek tüketicileri olan android.graphics.Bitmap nesnelerinin görüntülenmesi için özel destek sunar. İçeriğinin oluşturulmuş önizlemesini görmek için bir Bitmap örneğini tıklayın.

AHAT Bitmap Preview

Etkinlik sızıntıları sayfası

AHAT, Android'deki en yaygın ve etkili bellek sızıntılarından biri olan sızdırılmış Etkinlikleri tanımlamak için özel bir görünüme sahiptir.

  1. İşlem: MemoryLab'de Etkinlik Sızdır'a dokunun. Bu işlem, kendini kasıtlı olarak sızdıran LeakedActivity uygulamasını başlatır.
  2. Dump: Yığın dökümü alın.
  3. Analiz etme: AHAT kenar çubuğunda �Etkinlik Sızıntıları'nı tıklayın.
  4. Doğrulama: AHAT, com.android.memorylab.LeakedActivity öğesini sızdırılmış olarak listeler. Bunun nedeni, mDestroyed alanının doğru olması (Etkinlik yaşam döngüsünün sona erdiğini gösterir) ancak öğeye GC kökünden hâlâ erişilebilmesidir.

AHAT Activity Leaks Page

Yığın dökümlerini karşılaştırma

İki yığın dökümünü karşılaştırmak, bellek sorunlarını belirlemenin en etkili yollarından biridir. "Temiz" bir temel dökümü, bazı işlemler yapıldıktan sonra alınan bir dökümle karşılaştırarak hangi nesnelerin biriktiğini hemen görebilirsiniz.

Egzersiz: Fark karşılaştırmasıyla sızıntıları belirleme

  1. Temel: MemoryLab'i başlatın ve temel yığın dökümü alın:

    adb shell am dumpheap com.android.memorylab /data/local/tmp/base.hprof
    adb pull /data/local/tmp/base.hprof .
    
  2. İşlem: Uygulamada Java Belleği Ayır(10 MB) seçeneğine birkaç kez dokunun.

  3. Son: İkinci bir yığın dökümü alın:

    adb shell am dumpheap com.android.memorylab /data/local/tmp/leaked.hprof
    adb pull /data/local/tmp/leaked.hprof .
    
  4. Karşılaştırma: İkincisini birincil, ilkini ise referans olarak kullanarak AHAT'ı başlatın:

    java -jar out/host/linux-x86/framework/ahat.jar leaked.hprof --baseline base.hprof
    
  5. Analize Genel Bakış: Genel Bakış sayfasında artık Δ (Delta) sütunu bulunuyor. app yığını için büyük bir pozitif delta görürsünüz. Bu, önemli bir bellek büyümesi olduğunu gösterir.

Delta ile AHAT'a Genel Bakış

  1. Ayrıntılı İnceleme: Menüde rootlu'yu tıklayın. Bu sayfada, GC köklerinden erişilebilen nesneler tutulan boyutlarına göre sıralanmış şekilde gösterilir. En üstte büyük bir pozitif delta ile MainActivity simgesini görürsünüz.

AHAT Rooted View with Delta

Ayırma yığın izlemelerini kaydetme

GC kökünden örnek yol, bir nesnenin neden hâlâ canlı olduğunu açıklasa da nasıl oluşturulduğunu açıklamaz. Ayırma yığını izleri, bir nesneyi ayıran tam kod satırını sağlar.

Kavram ve Değişkenler: Her ayırmanın yığın izleme (stack trace) kaydetmek, hesaplama açısından maliyetlidir ve önemli ölçüde bellek tüketir. Büyük bir üretim uygulamasında bu durum, uygulamayı neredeyse kullanılamaz hale getirebilir. Ancak MemoryLab, tahsislerin kaynağını belirlemek için bu izlemeyi güvenli bir şekilde etkinleştirebileceğimiz kadar küçük bir uygulamadır.

Alıştırma: Bayt dizilerinin kaynağını belirleme

  1. İzlemeyle başlayın: MemoryLab'i durdurmaya zorlayın ve --track-allocation işaretiyle yeniden başlatın. Daha fazla bağlam elde etmek için varsayılan yığın derinliğini artırın.

    # Increase the allocation tracker's stack depth (requires a process restart)
    adb shell setprop dalvik.vm.allocTrackerMaxStack 16
    
    adb shell am force-stop com.android.memorylab
    adb shell am start --track-allocation -n com.android.memorylab/.MainActivity
    
  2. İşlem: Java Belleği Ayır(10 MB)'a birkaç kez dokunun.

  3. Dump: Yığın dökümü alıp çekin.

  4. Analiz et: Dökümü AHAT'ta açın. Büyük bir byte[] örneğine gidin. (ör. MainActivity → mJavaAllocations (ArrayList) → elementData (Object[]) → dizi öğesi [0] öğesini inceleyin).

  5. Doğrulama: Örnek görünümünde Ayrılan Site bölümüne bakın. Bu durumda, MainActivity.allocateJava ile sonuçlanan tam yığın izi gösterilir.

AHAT Allocation Site

Yinelenen dizeleri bulma ve hidrasyon şişkinliği

Bir uygulamada klasik GC-root sızıntıları olmasa bile canlı Java yığını, , Protobuf Cursor veya Room veritabanı seri durumdan çıkarma sırasında oluşturulan binlerce yinelenen java.lang.String örneği nedeniyle şişebilir. Tekrarlanan anahtarlar, durum dizeleri, kategori etiketleri veya URL'ler genellikle her ağ yanıtında ya da veritabanı sorgusunda yeniden ayrılır. Büyük feed, mesajlaşma ve içerik uygulamalarında, yinelenen dizeler rutin olarak canlı String belleğin% 30 ila% 60'ını oluşturur.

AHAT'ta yinelenen dizeleri incelemek için:

  1. Tahsisler sayfasını açın ve java.lang.String ile filtreleyin.
  2. --baseline ile iki yığın dökümünü karşılaştırırken bir feed'i doldurduktan veya yerel bir önbelleği yükledikten sonra java.lang.String örnek sayılarının ve toplam baytların orantısız bir şekilde artıp artmadığını kontrol edin.
  3. Birden fazla bellek içi model nesnesinde tutulan aynı dize değerlerini tespit etmek için java.lang.String örnekler tablosuna (boyuta veya değere göre sıralanmış) göz atın.
  • Çözüm: Çalışma zamanı dahili tablosu genel olduğundan ve kilit çekişmesine neden olabileceğinden veya dizeleri gerekenden daha uzun süre saklayabileceğinden, rastgele kullanıcı ya da ağ girişinde String.intern() işlevini rastgele çağırmaktan kaçının. Bunun yerine, sınırlı kapsamlı bir tekilleştirme önbelleği (ör. ayrıştırıcınızın veya bağdaştırıcınızın içindeki LruCache<String, String>) kullanarak seri durumdan çıkarma sırasında yüksek sıklıklı alan dizelerini tekilleştirin ya da sabit değer kümelerini enum'lar veya tam sayı sabitleri olarak temsil edin.

Java bellek dinamiklerini analiz etme (birleştirilmiş profil)

Bir uygulamanın bellek davranışı hakkında kapsamlı bilgi edinmek için bellek sayaçlarını, iş parçacığı etkinliğini ve çağrı yığınına dayalı ayırma profili oluşturmayı tek bir Perfetto izinde birleştirebilirsiniz. Bu sayede, sistem genelindeki bellek metriklerini (ör. RSS ve yığın boyutu) belirli kod yürütme ve ayırma siteleriyle ilişkilendirebilirsiniz.

Aşağıdakileri sağlayan birleşik bir yapılandırma kullanacağız:

  • Bellek sayaçları (linux.process_stats): RSS ve diğer bellek metriklerini yoklar.
  • ATrace (dalvik, memory, sched kategorileri): İş parçacığı durumlarını ve GC etkinliklerini yakalar.
  • Heapprofd (android.heapprofd): 5 saniyede bir sürekli dökümlerle hem com.android.art (Java) hem de libc.malloc (yerel) yığınları hedefler.

Alıştırma: Birleşik bellek analizi

Bu alıştırmada, MemoryLab uygulamasını çalıştırıp izdeki farklı kalıpları gözlemlemek için bir dizi bellek işlemi gerçekleştireceğiz:

  1. Temel: Boşta kalma durumu.
  2. Java Churn: Hemen çöp toplama işlemine tabi tutulan geçici tahsisler.
  3. Kalıcı Java Ayırma: Bellekte kalan Java nesnelerini ayırma.
  4. Bit eşlem Ayırma: Büyük grafik öğeleri ayırma (yerel yığın/ekran kartı belleğinde bulunur).
  5. Geri alma: Ayrılan tüm kaynakların serbest bırakılması.

1. Lansman ve hazırlık

  1. Temiz bir durum sağlamak için uygulamayı kapanmaya zorlayın ve yeniden başlatın:

    adb shell am force-stop com.android.memorylab
    adb shell am start -W -n com.android.memorylab/.MainActivity
    

2. İzlemeyi başlatma ve sırayı tetikleme

40 saniyelik bir izleme başlatıp am broadcast komutlarını kullanarak bellek etkinliklerini tetikleyeceğiz.

  1. İzlemeyi başlatın:

    adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/java_memory.perfetto-trace <<EOF
    buffers: {
        size_kb: 131072
        fill_policy: RING_BUFFER
    }
    data_sources: {
        config {
            name: "linux.process_stats"
            target_buffer: 0
            process_stats_config {
                scan_all_processes_on_start: true
                proc_stats_poll_ms: 100
            }
        }
    }
    data_sources: {
        config {
            name: "linux.ftrace"
            target_buffer: 0
            ftrace_config {
                ftrace_events: "sched/sched_switch"
                ftrace_events: "task/task_newtask"
                ftrace_events: "task/task_rename"
                ftrace_events: "ftrace/print"
                atrace_categories: "dalvik"
                atrace_categories: "am"
                atrace_categories: "res"
                atrace_categories: "memory"
                atrace_categories: "sched"
                atrace_apps: "com.android.memorylab"
            }
        }
    }
    data_sources: {
        config {
            name: "android.heapprofd"
            target_buffer: 0
            heapprofd_config {
                sampling_interval_bytes: 4096
                process_cmdline: "com.android.memorylab"
                heaps: "libc.malloc"
                heaps: "com.android.art"
                shmem_size_bytes: 8388608
                block_client: true
                continuous_dump_config {
                    dump_phase_ms: 1000
                    dump_interval_ms: 5000
                }
            }
        }
    }
    duration_ms: 40000
    EOF
    
  2. Sırayı tetikleyin (izleme çalışırken bu komutları ana makine terminalinizde önerilen zamanlamaya uyarak çalıştırın):

    # Wait ~5s for trace initialization, then start Java churn:
    adb shell am broadcast -a com.android.memorylab.CHURN_JAVA
    
    # Wait ~10s (at 15s mark), allocate 10MB of persistent Java memory:
    adb shell am broadcast -a com.android.memorylab.ALLOC_JAVA
    
    # Wait ~5s (at 20s mark), allocate 20MB of Bitmaps (native/graphics):
    adb shell am broadcast -a com.android.memorylab.LEAK_BITMAP
    
    # Wait ~10s (at 30s mark), free everything:
    adb shell am broadcast -a com.android.memorylab.FREE_ALL
    
  3. Alternatif (KSA Aracı): Profili doğrudan heap_profile komut dosyasını kullanarak da başlatabilirsiniz. Bu komut dosyası, sürekli dökümlerle hem Java hem de yerel yığınları hedefler:

    external/perfetto/tools/heap_profile -n com.android.memorylab \
      --heaps com.android.art,libc.malloc \
      -c 5000 \
      -d 40000 \
      -o java_memory_profile
    

3. Birleştirilmiş izi analiz etme

Toplanan java_memory.perfetto-trace dosyasını Perfetto kullanıcı arayüzünde açın.

Perfetto'daki temel izler

Zaman çizelgesini analiz etmeden önce com.android.memorylab işlemi için gerekli olan şu parçaları bulun:

  1. mem.rss.anon (Anonim RSS): İşlemin Bellek bölümünde bulunur. Bu izleme, işletim sistemi tarafından işleme ayrılan fiziksel belleği (RAM) ölçer. Gerçek bellek kullanımını gösterir.
  2. Heap size (KB): Bellek bölümünde de yer alır. Bu, Java yığını için ayrılmış sanal adres alanını temsil eden Dalvik/ART'ye özgü bir sayaçtır. Nesneler ayrıldıkça ve GC çalıştıkça dalgalanan VM'nin dahili yığın sınırını yansıtır.
  3. HeapTaskDaemon: İşlem altındaki ileti dizileri listesinde bulunur. Bu, ART çöp toplayıcının işinin büyük bir kısmını yaptığı arka plan iş parçacığıdır. Buradaki etkinlik, etkin GC kartlarını gösterir.
  4. Sürekli ayırma dökümleri (heapprofd): Üst zaman çizelgesi boyunca renkli dilimler olarak gösterilir. Her dilim, zaman içindeki bir süreyi temsil eder. Tek bir dilimi tıkladığınızda veya bir zaman aralığı seçtiğinizde, bu dönemde ne ayrıldığını görmek için com.android.art (Java ayırmaları) veya libc.malloc (yerel ayırmalar) için alev grafiğini (alt bölmede) inceleyebilirsiniz.

Kronolojik aşama analizi

Bu parçaların egzersizin her aşamasında nasıl etkileşime girdiğini görmek için izlemeyi kronolojik olarak inceleyelim.

1. aşama: referans noktası (0-5 saniye)
  • Ne oluyor?: Uygulama boşta duruyor ve komut bekliyor.
  • Parça Durumu:
    • mem.rss.anon: Taban çizgisi üzerinde düz çizgi (cihaza bağlı olarak genellikle 60-80 MB civarında).
    • Heap size (KB): İlk Java yığın ayırma işlemiyle eşleşen düz çizgi.
    • HeapTaskDaemon: Boşta (yürütme gösteren dilim yok).
    • Ayrım Dökümleri: Minimum temel ayırmaları gösterir.

1. aşama referans değerini gösteren Perfetto kullanıcı arayüzü

2. aşama: Java ayırma karmaşası (5 sn - 15 sn)
  • Neler oluyor?: AllocationChurnThread başlatılıyor, 1 MB'lık diziler tekrar tekrar ayrılıyor ve atılıyor.
  • Parça Durumu:
    • Heap size (KB): Hızlı bir testeredişi deseni gösteriyor. Yığın boyutu, tahsisler biriktikçe artar ve GC çalıştırıldığında keskin bir şekilde düşer.
    • HeapTaskDaemon: Yürütme dilimlerinin Heap size testere dişindeki düşüşlerle mükemmel şekilde hizalandığı, neredeyse sürekli bir etkinlik gösterir.
    • mem.rss.anon: Java yığın etkinliğini izler.
    • Java Yığın Ayırma Dökümleri: Bu parçadaki dilimleri seçtiğinizde com.android.art yığın ayırmaları gösterilir.

2. aşamadaki müşteri kaybını gösteren Perfetto kullanıcı arayüzü Ayrılan yer örnekleri, AllocationChurnThread öğesinin birincil yer ayırıcı olduğunu gösteriyor. Tüm ayrılan yerler, MainActivity.java içindeki lambda'yı işaret eden aynı çağrı yığınını paylaşıyor.

2. Aşama Java ayırmalarını gösteren Perfetto kullanıcı arayüzü

3. aşama: Kalıcı Java ayırma (15-20 saniye)
  • Ne oluyor?: 10 MB'lık Java nesnesi ayırıyoruz ve mJavaAllocations içinde bunlara referans tutuyoruz.
  • Parça Durumu:
    • Heap size (KB): Testere dişi şeklindeki adımların tabanı yaklaşık 10 MB artıyor.
    • mem.rss.anon: İşletim sistemi bu kalıcı ayırmayı yeni fiziksel sayfalarla desteklemesi gerektiğinden yaklaşık 10 MB artar.
    • Java Yığın Ayırma Dökümleri: Bu parçadaki dilimleri seçtiğinizde com.android.art yığın ayırmaları gösterilir.
    • Allocation Dumps (Flamegraph): Bu pencerede alınan döküm için com.android.art heap'i incelediğimizde, tutulan boyuta katkıda bulunan MainActivity.allocateJava ile başlayan yeni bir ayırma yolu gösteriliyor.

3. aşamada kalıcı Java ayırmayı gösteren Perfetto kullanıcı arayüzü Kalıcı ayırma için 10 MB'lık artışla çakışan bir süreyi kapsayan bir ayırma örneği seçin. Ayrılan çağrı yığınlarının iki farklı siteye ayrıldığını görmelisiniz. Bunlardan biri, daha önce gördüğümüz kısa ömürlü aynı ayrılan alan değişiminden, diğeri ise yeni uzun ömürlü ayrılan alandan sorumludur.

3. Aşama Java ayırmalarını gösteren Perfetto kullanıcı arayüzü

4. aşama: bit eşlem ayırma (20-30 saniye)
  • Ne oluyor? Ayrıca 20 MB bit eşlem de ayırıyoruz.
  • Parça Durumu:
    • Heap size (KB): Öncekiyle aynı.
    • mem.rss.anon: Bitmap piksel verileri için yerel ayırmalara karşılık gelen yaklaşık 20 MB'lık önemli bir artış gösterir.
    • Allocation Dumps (Flamegraph): Bu kez libc.malloc (Native) yığını için dilimlere odaklanın.

4. Aşama Bit Eşlem Ayırma'yı gösteren Perfetto kullanıcı arayüzü Yerel bellek ayırma çağrı yığınları, yerel grafik kitaplıklarından kaynaklanan bitmap bellek ayırmayı ortaya çıkarır. Bu, yerel ayırma izleme için iyi bir kullanım alanıdır. Çünkü bu Bitmap ayırmalarını Java yığınında görmezsiniz.

4. aşama yerel ayırmalarını gösteren Perfetto kullanıcı arayüzü

5. aşama: geri kazanım (30-40 yaş)
  • Ne oluyor?: FREE_ALL tetikleniyor. Bu işlemde, tüm kalıcı Java ayırmalarına ve bit eşlemlere yapılan referanslar temizleniyor, ardından açık bir System.gc() gerçekleştiriliyor.
  • Parça Durumu:
    • Heap size (KB): Standart seviyeye geri döner.
    • mem.rss.anon: İşletim sisteminin fiziksel sayfaları geri aldığını gösteren düşüş.
    • HeapTaskDaemon: Çöp toplama işlemi yapılırken son bir etkinlik patlaması gösterir.

5. Aşama Geri Kazanım'ı gösteren Perfetto kullanıcı arayüzü

Geçmiş OOM'leri izleme (ApplicationExitInfo)

LMK'yi gerçekleştiği sırada yakalamak etkin hata ayıklama için harika bir yöntemdir ancak saha telemetrisi için ApplicationExitInfo API'sini kullanabilirsiniz. Bu, uygulamanızın önceki bir oturumda neden sonlandırıldığını öğrenmesini sağlar.

ActivityManager am = getSystemService(ActivityManager.class);
List<ApplicationExitInfo> exitReasons = am.getHistoricalProcessExitReasons(null, 0, 1);
if (!exitReasons.isEmpty()) {
    ApplicationExitInfo info = exitReasons.get(0);
    if (info.getReason() == ApplicationExitInfo.REASON_LOW_MEMORY) {
        // App was killed by the system Low Memory Killer
    }
}

En iyi uygulamalar

  1. Önce Temel Değer: Uygulama başlatıldıktan sonra ancak test ettiğiniz işlemi gerçekleştirmeden önce her zaman bir "temel değer" yığın dökümü alın.
  2. AHAT'ın Etkinlik Sızıntıları Sayfasını Kullanma: AHAT, yok edilen ancak bellekte tutulmaya devam eden Etkinlik örneklerini otomatik olarak tanımlayan özel bir Etkinlik Sızıntıları sayfası içerir. Bu genellikle yaygın sızıntıları bulmanın en hızlı yoludur.
  3. GC köklerine giden yolu kontrol edin: Sızdırılan tüm nesneler için, hangi referansın nesneyi canlı tuttuğunu (ör. statik alan, uzun süren iş parçacığı veya kayıtlı dinleyici) tam olarak anlamak için AHAT'taki Kökten Giden Yol görünümünü kullanın.

← Araçlar | ↑ Yukarı | Bit eşlemler →