تحلیل حافظه جاوا

برنامه‌های جاوا و کاتلین حافظه را از طریق یک هیپ جمع‌آوری زباله مدیریت می‌کنند. وقتی اشیاء دیگر قابل دسترسی نباشند، جمع‌آوری زباله (GC) در نهایت فضای آنها را پس می‌گیرد. نشت حافظه زمانی رخ می‌دهد که اشیاء که دیگر مورد نیاز نیستند هنوز توسط "ریشه‌های GC" نگهداری می‌شوند و از بازپس‌گیری آنها جلوگیری می‌شود.

مفاهیم اصلی

ریشه‌های GC

ریشه GC نوع خاصی از شیء است که زباله‌روب آن را همیشه قابل دسترسی می‌داند. مثال‌ها عبارتند از:

  • نخ‌های فعال (و اشیایی که از فریم‌های پشته جاوای در حال اجرای آنها ارجاع داده می‌شوند).
  • کلاس‌هایی با متدهای اجرای فعال.
  • ارجاعات JNI (ارجاعات سراسری یا محلی که توسط کد بومی نگهداری می‌شوند).

مسیر به ریشه GC

تا زمانی که زنجیره‌ای از ارجاعات از یک ریشه GC به یک شیء وجود داشته باشد، آن شیء "قابل دسترسی" است و نمی‌تواند جمع‌آوری زباله شود. این زنجیره، مسیر به ریشه GC نامیده می‌شود. برای رفع نشت حافظه، باید این زنجیره را شناسایی و بشکنید.

مسیر به ریشه GC

درختان سلطه‌گر

اگرچه مسیر ریشه GC به شما می‌گوید که چرا یک شیء زنده است، اما به شما نمی‌گوید که در صورت خراب شدن آن مرجع، چه مقدار از حافظه بازیابی می‌شود. برای این کار، ما از Dominator Trees استفاده می‌کنیم.

گفته می‌شود شیء A بر شیء B تسلط دارد اگر هر مسیری از هر ریشه GC به B باید از A عبور کند. اگر A بر B تسلط داشته باشد، پس بازپس‌گیری A تضمین می‌کند که B نیز می‌تواند بازپس گرفته شود، زیرا هیچ مسیر دیگری از هیچ ریشه‌ای به B وجود ندارد.

نمودار زیر یک گراف شیء و درخت غالب متناظر با آن را نشان می‌دهد. توجه کنید که چگونه در این گراف، A و B به شیء D می‌رسند، بنابراین نه A و نه B بر D غالب نیستند؛ در عوض، ریشه GC نزدیکترین غالب به آن است.

درخت سلطه‌گر

به دست آوردن داده‌های هیپ (heap dump) جاوا

یک heap dump تصویری از تمام اشیاء موجود در heap جاوا در یک نقطه زمانی خاص است.

استفاده از ADB

برای گرفتن یک فایل heap dump از یک فرآیند در حال اجرا، می‌توانید نام بسته را مستقیماً به am dumpheap ارسال کنید. برای اجرای این دستور، باید برنامه خود را با <profileable android:shell="true"/> یا <debuggable> بسازید.

# 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 همچنین می‌تواند با فعال کردن منبع داده android.java_hprof در پیکربندی Perfetto شما، داده‌های هیپ جاوا را به عنوان بخشی از یک ردیابی در سطح سیستم ضبط کند. این برای مرتبط کردن وضعیت هیپ با سایر رویدادهای سیستم مفید است.

برای گرفتن یک هیپ دامپ برای برنامه MemoryLab با استفاده از Perfetto، می‌توانید از دستور زیر استفاده کنید:

# 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 .

تجزیه و تحلیل با AHAT

ابزار AHAT (ابزار تحلیل هیپ اندروید) ابزار پیشنهادی برای مشاهده فایل‌های .hprof در مرورگر وب است.

شروع AHAT

اگر ahat را در مسیر خود نصب کرده‌اید، آن را با دستور زیر اجرا کنید:

ahat heap.hprof

یا فایل jar مستقل را اجرا کنید:

java -jar ahat.jar heap.hprof

سپس مرورگر خود را باز کنید و به آدرس http://localhost:7100 بروید .

برای جزئیات بیشتر در مورد به دست آوردن یا ساخت AHAT، به مخزن منبع AHAT مراجعه کنید.

گردش‌های کاری تحلیل کلیدی

پیدا کردن نشتی‌ها

در نمای Allocations، کلاس Activity خود ( MainActivity ) را جستجو کنید.

نمای AHAT که نمونه‌ها را نشان می‌دهد

برای یافتن همه نمونه‌ها ، روی کلاس کلیک کنید.

نمای AHAT که نمونه‌های MainActivity را نشان می‌دهد برای بررسی نمونه‌ی MainActivity روی آن کلیک کنید.

AHAT که جزئیات یک نمونه را نشان می‌دهد

در نمای نمونه، می‌توانید مسیر نمونه از GC Root را پیدا کنید، که زنجیره ارجاعاتی را نشان می‌دهد که مانع از جمع‌آوری زباله توسط شیء می‌شود، و اندازه شیء ، که نشان می‌دهد این نمونه خاص چه مقدار حافظه را حفظ می‌کند.

مسیر نمونه AHAT از ریشه GC و اندازه شیء

تجزیه و تحلیل بیت‌مپ‌ها

AHAT پشتیبانی ویژه‌ای برای مشاهده اشیاء android.graphics.Bitmap دارد که اغلب مصرف‌کنندگان حافظه زیادی هستند. برای دیدن پیش‌نمایش رندر شده از محتوای آن، روی یک نمونه Bitmap کلیک کنید.

پیش‌نمایش بیت‌مپ AHAT

صفحه نشت فعالیت

AHAT یک دیدگاه تخصصی برای شناسایی فعالیت‌های نشت‌شده دارد که یکی از رایج‌ترین و تأثیرگذارترین نشت‌های حافظه در اندروید هستند.

  1. اقدام : در MemoryLab، روی گزینه‌ی Leak an Activity (نشت یک فعالیت) ضربه بزنید. این کار LeakedActivity را اجرا می‌کند که عمداً خودش را نشت می‌دهد.
  2. تخلیه : یک تخلیه هیپ انجام دهید.
  3. تحلیل : روی Activity Leaks در نوار کناری AHAT کلیک کنید.
  4. تأیید : AHAT، com.android.memorylab.LeakedActivity به عنوان نشت‌شده فهرست می‌کند، زیرا فیلد mDestroyed آن درست است (نشان می‌دهد که چرخه حیات Activity به پایان رسیده است) اما هنوز از یک ریشه GC قابل دسترسی است.

صفحه نشت فعالیت‌های AHAT

انواع مختلف هیپ دامپ

مقایسه دو هیپ دامپ یکی از قدرتمندترین راه‌ها برای شناسایی مشکلات حافظه است. با مقایسه یک بیس لاین دامپ «تمیز» با دامپی که پس از انجام برخی اقدامات گرفته شده است، می‌توانید بلافاصله ببینید کدام اشیاء انباشته شده‌اند.

تمرین: شناسایی نشتی‌ها از طریق تفاوت‌گذاری

  1. خط پایه : MemoryLab را اجرا کنید و یک هیپ دامپ خط پایه بگیرید:

    adb shell am dumpheap com.android.memorylab /data/local/tmp/base.hprof
    adb pull /data/local/tmp/base.hprof .
    
  2. اقدام : چندین بار در برنامه روی گزینه «اختصاص حافظه جاوا (10 مگابایت)» ضربه بزنید.

  3. نهایی : یک دامپ هیپ دوم بگیرید:

    adb shell am dumpheap com.android.memorylab /data/local/tmp/leaked.hprof
    adb pull /data/local/tmp/leaked.hprof .
    
  4. مقایسه : AHAT را با دومین dump به عنوان primary و اولین dump به عنوان baseline شروع کنید:

    java -jar out/host/linux-x86/framework/ahat.jar leaked.hprof --baseline base.hprof
    
  5. تحلیل نمای کلی : صفحه نمای کلی اکنون شامل یک ستون Δ (دلتا) است. شما یک دلتای مثبت بزرگ برای پشته app خواهید دید که نشان دهنده رشد قابل توجه حافظه است.

بررسی اجمالی AHAT با دلتا

  1. بررسی دقیق : در منو روی root کلیک کنید. این صفحه اشیاء قابل دسترسی از ریشه‌های GC را نشان می‌دهد که بر اساس اندازه حفظ شده‌شان مرتب شده‌اند. در بالا MainActivity با یک دلتای مثبت بزرگ خواهید دید.

نمای ریشه‌دار AHAT با دلتا

ضبط ردپاهای تخصیص پشته

در حالی که مسیر نمونه از GC Root به شما می‌گوید که چرا یک شیء هنوز زنده است، اما به شما نمی‌گوید که چگونه ایجاد شده است. ردیابی‌های پشته تخصیص، خط دقیق کدی را که یک شیء را تخصیص داده است، ارائه می‌دهند.

مفهوم و بده‌بستان‌ها : ثبت رد پشته هر تخصیص از نظر محاسباتی پرهزینه است و حافظه قابل توجهی مصرف می‌کند. در یک برنامه تولیدی بزرگ، این می‌تواند برنامه را تقریباً غیرقابل استفاده کند. با این حال، MemoryLab یک برنامه به اندازه کافی کوچک است که می‌توانیم با خیال راحت این ردیابی را برای مشخص کردن منبع تخصیص‌ها فعال کنیم.

تمرین: شناسایی منبع آرایه‌های بایتی

  1. شروع با ردیابی : MemoryLab را متوقف کرده و آن را با پرچم --track-allocation مجدداً راه‌اندازی کنید. عمق پشته پیش‌فرض را افزایش دهید تا زمینه بیشتری را در بر بگیرد.

    # 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. اقدام : چند بار روی «اختصاص حافظه جاوا (10 مگابایت)» ضربه بزنید.

  3. تخلیه : یک توده تخلیه بردارید و آن را بکشید.

  4. تحلیل : فایل dump را در AHAT باز کنید. به یک نمونه byte[] بزرگ بروید. (مثلاً، بررسی کنید MainActivitymJavaAllocations ( ArrayList ) → elementData ( Object[] ) → array element [0] ).

  5. تأیید : در نمای نمونه، به بخش Allocation Site نگاه کنید. این بخش، ردیابی کامل پشته منتهی به MainActivity.allocateJava را نشان می‌دهد.

محل تخصیص AHAT

تحلیل دینامیک حافظه جاوا (پروفایل ترکیبی)

برای به دست آوردن تصویر کاملی از رفتار حافظه یک برنامه، می‌توانید شمارنده‌های حافظه، فعالیت نخ‌ها و پروفایل تخصیص مبتنی بر callstack را در یک ردیابی Perfetto واحد ترکیب کنید. این به شما امکان می‌دهد معیارهای حافظه در سطح سیستم (مانند RSS و اندازه پشته) را با سایت‌های اجرای کد و تخصیص خاص مرتبط کنید.

ما از یک پیکربندی ترکیبی استفاده خواهیم کرد که موارد زیر را فعال می‌کند:

  • شمارنده‌های حافظه ( linux.process_stats ): نظرسنجی‌های RSS و سایر معیارهای حافظه.
  • ATrace ( dalvik ، memory ، sched categories): حالت‌های نخ و رویدادهای GC را ثبت می‌کند.
  • Heapprofd ( android.heapprofd ): هر دو هیپ com.android.art (جاوا) و libc.malloc (بومی) را با تخلیه مداوم داده‌ها هر ۵ ثانیه یک بار هدف قرار می‌دهد.

تمرین: تحلیل حافظه ترکیبی

در این تمرین، برنامه MemoryLab را اجرا خواهیم کرد و یک توالی از عملیات حافظه را برای مشاهده الگوهای مختلف در ردیابی انجام خواهیم داد:

  1. حالت پایه : حالت بیکار.
  2. Java Churn : تخصیص‌های موقتی که بلافاصله زباله‌روب می‌شوند.
  3. تخصیص پایدار جاوا : تخصیص اشیاء جاوا که در حافظه باقی می‌مانند.
  4. تخصیص بیت‌مپ : تخصیص منابع گرافیکی بزرگ (که در حافظه هیپ/گرافیک بومی قرار دارند).
  5. بازپس‌گیری : آزاد کردن تمام منابع اختصاص داده شده.

۱. راه‌اندازی و آماده‌سازی

  1. برای اطمینان از پاک شدن برنامه، آن را متوقف کرده و مجدداً راه‌اندازی کنید:

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

۲. شروع ردیابی و اجرای توالی

ما یک ردیابی ۴۰ ثانیه‌ای را شروع می‌کنیم و رویدادهای حافظه را با استفاده از دستورات am broadcast فعال می‌کنیم.

  1. ردیابی را شروع کنید :

    adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/java_memory.perfetto-trace <<EOF
    buffers: {
        size_kb: 65536
        fill_policy: RING_BUFFER
    }
    data_sources: {
        config {
            name: "android.java_hprof"
            java_hprof_config {
                process_cmdline: "com.example.myapp"
            }
        }
    }
    duration_ms: 10000
    EOF
    
  2. دنباله را فعال کنید (این دستورات را در ترمینال میزبان خود در حالی که ردیابی در حال اجرا است، با رعایت زمان‌بندی پیشنهادی اجرا کنید):

    # 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. جایگزین (ابزار CLI) : همچنین می‌توانید پروفایل را مستقیماً با استفاده از اسکریپت heap_profile شروع کنید و هم هیپ‌های جاوا و هم هیپ‌های بومی را با دامپ‌های مداوم هدف قرار دهید:

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

۳. تحلیل رد ترکیبی

فایل java_memory.perfetto-trace جمع‌آوری‌شده را در رابط کاربری Perfetto باز کنید.

آهنگ‌های کلیدی در پرفتو

قبل از تجزیه و تحلیل جدول زمانی، این مسیرهای ضروری را برای فرآیند com.android.memorylab پیدا کنید:

  1. mem.rss.anon (RSS ناشناس) : در بخش حافظه فرآیند یافت می‌شود. این مسیر، حافظه فیزیکی (RAM) اختصاص داده شده به فرآیند توسط سیستم عامل را اندازه‌گیری می‌کند. این مقدار، میزان واقعی اشغال حافظه را نشان می‌دهد.
  2. Heap size (KB) : همچنین در بخش حافظه قرار دارد. این یک شمارنده مخصوص Dalvik/ART است که فضای آدرس مجازی رزرو شده برای هیپ جاوا را نشان می‌دهد. این شمارنده محدودیت هیپ داخلی ماشین مجازی را نشان می‌دهد که با اختصاص اشیاء و اجرای GC تغییر می‌کند.
  3. HeapTaskDaemon : در لیست نخ‌های زیر فرآیند یافت می‌شود. این نخ پس‌زمینه‌ای است که زباله‌روب ART بیشتر کار خود را در آن انجام می‌دهد. فعالیت در اینجا نشان‌دهنده‌ی پاس‌های فعال GC است.
  4. تخلیه‌های تخصیص پیوسته (heapprofd) : به صورت برش‌های رنگی در امتداد خط زمانی بالا نشان داده شده است. هر برش نشان دهنده یک مدت زمان است. کلیک بر روی یک برش یا انتخاب یک محدوده زمانی به شما امکان می‌دهد تا Flamegraph (در پنل پایین) را برای com.android.art (تخصیص‌های جاوا) یا libc.malloc (تخصیص‌های بومی) بررسی کنید تا ببینید در آن دوره چه چیزی اختصاص داده شده است.

تحلیل فاز زمانی

بیایید این رد را به ترتیب زمانی بررسی کنیم تا ببینیم این ردها چگونه در هر مرحله از تمرین با هم تعامل دارند.

مرحله ۱: خط پایه (۰ تا ۵ ثانیه)
  • چه اتفاقی می‌افتد : برنامه غیرفعال است و منتظر دستورات است.
  • وضعیت آهنگ :
    • mem.rss.anon : خط ثابت در خط پایه (معمولاً حدود ۶۰ تا ۸۰ مگابایت بسته به دستگاه).
    • Heap size (KB) : خط صاف، مطابق با تخصیص اولیه هیپ جاوا.
    • HeapTaskDaemon : بیکار (هیچ برشی اجرا را نشان نمی‌دهد).
    • تخلیه‌های تخصیص : حداقل تخصیص‌های پایه را نشان می‌دهد.

رابط کاربری Perfeto که خط پایه فاز ۱ را نشان می‌دهد

مرحله ۲: کاهش تخصیص جاوا (۵ تا ۱۵ ثانیه)
  • چه اتفاقی می‌افتد : AllocationChurnThread شروع به کار می‌کند و به طور مکرر آرایه‌های ۱ مگابایتی را تخصیص داده و آنها را دور می‌ریزد.
  • وضعیت آهنگ :
    • Heap size (KB) : یک الگوی دندانه اره‌ای سریع را نشان می‌دهد. اندازه هیپ با تجمع تخصیص‌ها افزایش می‌یابد و با اجرای GC به شدت کاهش می‌یابد.
    • HeapTaskDaemon : فعالیت تقریباً ثابتی را نشان می‌دهد، به طوری که برش‌های اجرایی کاملاً با قطرات در دندانه اره‌ای Heap size هم‌تراز می‌شوند.
    • mem.rss.anon : فعالیت حافظه هیپ جاوا را ردیابی می‌کند.
    • تخلیه‌های تخصیص هیپ جاوا : انتخاب برش‌ها در این مسیر، تخصیص‌های هیپ com.android.art را نشان می‌دهد.

رابط کاربری Perfetto که ریزش فاز ۲ را نشان می‌دهد نمونه‌های تخصیص AllocationChurnThread به عنوان تخصیص‌دهنده اصلی نشان می‌دهند، و همه تخصیص‌ها از یک callstack مشترک استفاده می‌کنند که به لامبدا درون MainActivity.java اشاره می‌کند.

رابط کاربری Perfeto که تخصیص‌های جاوای فاز ۲ را نشان می‌دهد

مرحله ۳: تخصیص مداوم جاوا (۱۵ تا ۲۰ ثانیه)
  • چه اتفاقی می‌افتد : ما 10 مگابایت از اشیاء جاوا را اختصاص می‌دهیم و در mJavaAllocations به آنها ارجاع می‌دهیم.
  • وضعیت آهنگ :
    • Heap size (KB) : اندازه پایه دندانه اره‌ای تقریباً 10 مگابایت افزایش می‌یابد.
    • mem.rss.anon : تقریباً ۱۰ مگابایت افزایش می‌یابد، زیرا سیستم‌عامل باید این تخصیص پایدار را با صفحات فیزیکی جدید پشتیبانی کند.
    • تخلیه‌های تخصیص هیپ جاوا : انتخاب برش‌ها در این مسیر، تخصیص‌های هیپ com.android.art را نشان می‌دهد.
    • تخلیه‌های تخصیص (Flamegraph) : بررسی هیپ com.android.art برای تخلیه‌ای که در این پنجره انجام شده است، یک مسیر تخصیص جدید از MainActivity.allocateJava را نشان می‌دهد که در اندازه حفظ شده نقش دارد.

رابط کاربری Perfetto که تخصیص جاوای پایدار فاز ۳ را نشان می‌دهد یک نمونه تخصیص را انتخاب کنید که مدت زمانی را پوشش دهد که با افزایش ۱۰ مگابایتی برای تخصیص پایدار همپوشانی داشته باشد. باید ببینید که callstack های تخصیص به دو سایت مختلف واگرا می‌شوند، یکی مسئول همان ریزش تخصیص کوتاه مدت که قبلاً دیدیم و دیگری مسئول تخصیص طولانی مدت جدید است.

رابط کاربری Perfeto که تخصیص‌های جاوای فاز ۳ را نشان می‌دهد

مرحله ۴: تخصیص بیت‌مپ (۲۰ تا ۳۰ ثانیه)
  • چه اتفاقی می‌افتد : ما همچنین 20 مگابایت Bitmaps اختصاص می‌دهیم.
  • وضعیت آهنگ :
    • Heap size (KB) : مانند قبل.
    • mem.rss.anon : افزایش قابل توجهی در حدود ۲۰ مگابایت را نشان می‌دهد که مربوط به تخصیص‌های بومی برای داده‌های پیکسلی Bitmap است.
    • تخلیه‌های تخصیص (Flamegraph) : این بار روی برش‌های مربوط به هیپ libc.malloc (بومی) تمرکز کنید.

رابط کاربری Perfetto که تخصیص بیت‌مپ فاز ۴ را نشان می‌دهد پشته‌های فراخوانی تخصیص بومی، تخصیص Bitmap را که از کتابخانه‌های گرافیکی بومی سرچشمه می‌گیرد، آشکار می‌کنند. این یک مورد استفاده خوب برای ردیابی تخصیص بومی است، زیرا این تخصیص‌های Bitmap را در پشته جاوا مشاهده نخواهید کرد.

رابط کاربری Perfeto که تخصیص‌های بومی فاز ۴ را نشان می‌دهد

مرحله ۵: احیا (۳۰ تا ۴۰ سال)
  • چه اتفاقی می‌افتد : ما FREE_ALL فعال می‌کنیم، که ارجاعات به تمام تخصیص‌ها و بیت‌مپ‌های پایدار جاوا را پاک می‌کند، و به دنبال آن یک System.gc() صریح اجرا می‌شود.
  • وضعیت آهنگ :
    • Heap size (KB) : به سطح پایه کاهش می‌یابد.
    • mem.rss.anon : به پایین برمی‌گردد و نشان می‌دهد که سیستم‌عامل صفحات فیزیکی را بازیابی می‌کند.
    • HeapTaskDaemon : آخرین فعالیت‌ها را هنگام پردازش جمع‌آوری زباله نشان می‌دهد.

رابط کاربری Perfetto که احیای فاز ۵ را نشان می‌دهد

نظارت بر خروجی‌های گذشته (ApplicationExitInfo)

دریافت LMK در حین وقوع، برای اشکال‌زدایی فعال عالی است، اما برای اندازه‌گیری از راه دور فیلد، می‌توانید از API ApplicationExitInfo استفاده کنید. این به برنامه شما اجازه می‌دهد تا دلیل خاتمه یافتن آن در یک جلسه قبلی را کشف کند.

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
    }
}

بهترین شیوه‌ها

  1. ابتدا در سطح پایه : همیشه پس از مقداردهی اولیه برنامه و قبل از انجام عملی که در حال آزمایش آن هستید، یک هیپ دامپ «سطح پایه» تهیه کنید.
  2. از صفحه نشت فعالیت AHAT استفاده کنید : AHAT شامل یک صفحه اختصاصی نشت فعالیت است که به طور خودکار نمونه‌های فعالیتی را که از بین رفته‌اند اما هنوز در حافظه نگهداری می‌شوند، شناسایی می‌کند. این اغلب سریع‌ترین راه برای یافتن نشت‌های رایج است.
  3. بررسی مسیر به ریشه‌های GC : برای هر شیء نشت‌شده، از نمای مسیر از ریشه در AHAT استفاده کنید تا دقیقاً بفهمید کدام مرجع آن را زنده نگه می‌دارد (مثلاً یک فیلد استاتیک، یک رشته طولانی مدت یا یک شنونده ثبت‌شده).

← ابزارها | ↑ بالا | بیت‌مپ‌ها →