برنامههای Java و Kotlin حافظه را ازطریق پشته جمعآوری زباله مدیریت میکنند. وقتی اشیاء دیگر قابلدسترسی نباشند، جمعآوریکننده زباله (GC) درنهایت فضای آنها را بازیابی میکند. نشت حافظه زمانی رخ میدهد که اشیایی که دیگر موردنیاز نیستند همچنان توسط «ریشههای GC» نگهداری میشوند و از بازیابی آنها جلوگیری میشود.
مفاهیم اصلی
ریشههای جمعآوری زباله
GC Root نوع خاصی از شیء است که زبالهروب آن را بهعنوان همیشه دردسترس درنظر میگیرد. چند نمونه:
- رشتههای فعال (و اشیایی که از چارچوبهای پشتهای Java درحال اجرای فعلیشان به آنها ارجاع داده شده است).
- کلاسهایی که روشهای فعال دارند.
- مرجعهای JNI (مرجعهای جهانی یا محلی که توسط کد بومی نگهداری میشوند).
مسیر به ریشه جمعآوری زباله
تا زمانی که زنجیرهای از ارجاعها از GC Root به یک شیء وجود داشته باشد، آن شیء «قابلدسترسی» است و نمیتوان آن را جمعآوری زباله کرد. این زنجیره مسیر به ریشه GC نامیده میشود. برای رفع نشت حافظه، باید این زنجیره را شناسایی و قطع کنید.

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

درحال دریافت رونوشتهای پشته جاوا
رونوشت پشته عکس فوری از همه اشیاء در پشته Java در یک نقطه زمانی خاص است.
استفاده از ADB
برای ضبط رونوشت پشته از پردازش درحال اجرا، میتوانید نام بسته را مستقیماً به 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
Perfetto همچنین میتواند رونوشتهای پشته Java را بهعنوان بخشی از ردیابی سراسری سیستم با فعال کردن منبع داده 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 (ابزار تجزیهوتحلیل پشته Android) ابزار توصیهشده برای مشاهده فایلهای .hprof در مرورگر وب است.
درحال شروع «زمان رسیدگی به کمکهای هوش مصنوعی»
اگر ahat را در مسیرتان نصب کردهاید، آن را با این دستور راهاندازی کنید:
ahat heap.hprof
یا jar خوداتکا را اجرا کنید:
java -jar ahat.jar heap.hprof
سپس مرورگرتان را در http://localhost:7100 باز کنید.
برای جزئیات مربوط به دریافت یا ساخت AHAT، به مخزن منبع AHAT مراجعه کنید.
گردش کارهای تحلیل کلیدی
درحال پیدا کردن نشتیها
کلاس «فعالیت» (MainActivity) خود را در نمای تخصیصها جستجو کنید.

روی کلاس کلیک کنید تا همه نمونهها را پیدا کنید.
روی نمونه
MainActivity کلیک کنید تا آن را بازرسی کنید.

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

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

صفحه نشت فعالیت
AHAT نمای تخصصیای برای شناسایی «فعالیتهای» نشتکرده دارد که یکی از رایجترین و تأثیرگذارترین نشتهای حافظه در Android است.
- کنش: در MemoryLab، روی Leak an Activity (لو دادن فعالیت) تکضرب بزنید. این کار
LeakedActivityرا راهاندازی میکند که عمداً خودش را نشت میدهد. - Dump: رونوشت پشته بگیرید.
- تجزیهوتحلیل: روی نشت فعالیت در نوار کناری AHAT کلیک کنید.
- درستیسنجی: AHAT
com.android.memorylab.LeakedActivityرا بهعنوان نشتکرده فهرست میکند زیرا فیلدmDestroyedآن درست است (نشاندهنده پایان چرخه حیات «فعالیت») اما همچنان از ریشه GC قابلدسترسی است.

درحال مقایسه رونوشتهای پشته
مقایسه دو رونوشت پشته یکی از قدرتمندترین روشها برای شناسایی مشکلات حافظه است. با مقایسه یک تخلیه پایه «تمیز» با تخلیهای که پساز انجام برخی اقدامات گرفته شده است، میتوانید فوراً ببینید کدام اشیاء انباشته شدهاند.
تمرین: شناسایی نشتها ازطریق Diffing
مقدار پایه: MemoryLab را راهاندازی کنید و رونوشت پشته مقدار پایه را بگیرید:
adb shell am dumpheap com.android.memorylab /data/local/tmp/base.hprof adb pull /data/local/tmp/base.hprof .کنش: چند بار روی اختصاص حافظه Java (۱۰ مگابایت) در برنامه ضربه بزنید.
نهایی: رونوشت پشته دوم را بگیرید:
adb shell am dumpheap com.android.memorylab /data/local/tmp/leaked.hprof adb pull /data/local/tmp/leaked.hprof .مقایسه: AHAT را با دومین تخلیه بهعنوان اصلی و اولین تخلیه بهعنوان خط مبنا شروع کنید:
java -jar out/host/linux-x86/framework/ahat.jar leaked.hprof --baseline base.hprofتحلیل نمای کلی: صفحه نمای کلی اکنون شامل ستون Δ (دلتا) است. تغییر مثبت بزرگی برای توده
appخواهید دید که نشاندهنده رشد قابلتوجه حافظه است.

- Drill Down (بررسی جزئیات): روی rooted (ریشهدار) در منو کلیک کنید. این صفحه اشیای قابلدسترسی از ریشههای GC را نشان میدهد که براساس اندازه حفظشدهشان مرتب شدهاند.
MainActivityرا در بالا با دلتای مثبت بزرگ خواهید دید.

درحال ضبط ردیابی پشتهای تخصیص
اگرچه مسیر نمونه از GC Root به شما میگوید چرا یک شیء هنوز زنده است، به شما نمیگوید چگونه ایجاد شده است. ردیابیهای پشته تخصیص، خط دقیق کدی را که شیء را تخصیص داده است ارائه میدهند.
مفهوم و موازنهها: ضبط کردن ردیابی پشته هر تخصیص ازنظر محاسباتی پرهزینه است و حافظه قابلتوجهی مصرف میکند. در یک برنامه تولیدی بزرگ، این مشکل میتواند برنامه را تقریباً غیرقابلاستفاده کند. بااینحال، MemoryLab برنامه کوچکی است که میتوانیم این ردیابی را بهطور ایمن فعال کنیم تا منبع تخصیصها را بهطور دقیق مشخص کنیم.
تمرین: شناسایی منبع آرایههای بایت
شروع با ردیابی: 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کنش: چندبار روی اختصاص دادن حافظه Java (۱۰ مگابایت) تکضرب بزنید.
Dump: رونوشت پشته بگیرید و آن را بکشید.
تحلیل: رونوشت را در AHAT باز کنید. به نمونه بزرگ
byte[]پیمایش کنید. (مثلاً، بازرسیMainActivity←mJavaAllocations(ArrayList) ←elementData(Object[]) ← عنصر آرایه[0]).درستیسنجی: در نمای نمونه، به بخش سایت تخصیص نگاه کنید. رد پشته کامل منتهی به
MainActivity.allocateJavaرا نشان میدهد.

شکار رشتههای تکراری و تورم آبرسانی
حتی وقتی برنامهای نشت GC-root کلاسیک ندارد، انبوهه Java زنده آن میتواند
با هزاران نمونه تکراری java.lang.String که درطول
غیرسریالیسازی JSON، Protobuf، Cursor، یا پایگاه داده Room ایجاد شدهاند، متورم شود. کلیدهای تکراری،
رشتههای وضعیت، برچسبهای دسته، یا نشانیهای وب اغلب در هر
پاسخ شبکه یا پرسمان پایگاه داده دوباره اختصاص داده میشوند. در برنامههای بزرگ فید، پیامرسانی، و محتوا، رشتههای تکراری معمولاً ۳۰٪ تا ۶۰٪ از حافظه String زنده را تشکیل میدهند.
برای بررسی رشتههای تکراری در AHAT:
- صفحه تخصیصها را باز کنید و براساس
java.lang.Stringفیلتر کنید. - هنگام مقایسه دو رونوشت پشته با
--baseline، بررسی کنید که آیاjava.lang.Stringتعداد نمونهها و کل بایتها بهطور نامتناسبی پساز هیدراته کردن فید یا بار کردن حافظه نهان محلی افزایش مییابد یا نه. - جدول نمونههای
java.lang.Stringرا (مرتبشده براساس اندازه یا مقدار) مرور کنید تا مقادیر رشتهای یکسانی را که در چندین شیء مدل درونحافظهای حفظ شدهاند شناسایی کنید.
- راهحل: از فراخوانی بیرویه
String.intern()در ورودیهای دلخواه کاربر یا شبکه خودداری کنید، زیرا جدول داخلی زمان اجرا سراسری است و میتواند باعث رقابت قفل شود یا رشتهها را طولانیتر از حد نیاز نگه دارد. درعوض، رشتههای دامنه با بسامد بالا را درطول واگرداندن بااستفاده از حافظه پنهان حذف تکرار محدود و محدودشده (مانندLruCache<String, String>درون تجزیهکننده یا آداپتور) تکرارزدایی کنید، یا مجموعههای ثابت مقادیر را بهعنوان شمارشها یا ثابتهای عدد صحیح نشان دهید.
درحال تحلیل پویایی حافظه جاوا (نمایه ترکیبی)
برای دریافت تصویری کامل از رفتار حافظه برنامه، میتوانید شمارندههای حافظه، فعالیت رشته، و نمایهسازی تخصیص مبتنی بر پشته فراخوانی را در یک ردیابی Perfetto ترکیب کنید. این کار به شما امکان میدهد سنجههای حافظه سراسر سیستم (مانند RSS و اندازه توده) را با سایتهای اختصاص و اجرای کد خاص مرتبط کنید.
از پیکربندی ترکیبی استفاده خواهیم کرد که موارد زیر را فعال میکند:
- شمارندههای حافظه (
linux.process_stats): RSS و سایر سنجههای حافظه را نظرسنجی میکند. - ATrace (دستههای
dalvik،memory،sched): وضعیتهای رشته و رویدادهای GC را ضبط میکند. - Heapprofd (
android.heapprofd): هر دو پشتهcom.android.art(Java) وlibc.malloc(بومی) را با رونوشتهای مداوم هر ۵ ثانیه هدف قرار میدهد.
تمرین: تجزیهوتحلیل حافظه ترکیبی
در این تمرین، برنامه MemoryLab را اجرا میکنیم و دنبالهای از عملیات حافظه را انجام میدهیم تا الگوهای مختلف را در ردگیری مشاهده کنیم:
- مبنا: حالت آزاد.
- Java Churn: تخصیصهای موقتی که بلافاصله جمعآوری زباله میشوند.
- تخصیص جاوا پایدار: تخصیص اشیاء جاوا که در حافظه باقی میمانند.
- تخصیص بیتمپ: تخصیص داراییهای گرافیکی بزرگ (که در حافظه گرافیکی/ توده بومی قرار دارند).
- احیا: آزاد کردن همه منابع اختصاصدادهشده.
۱. راهاندازی و آمادهسازی
برنامه را اجباراً متوقف و بازراهاندازی کنید تا مطمئن شوید که در وضعیت تمیز قرار دارد:
adb shell am force-stop com.android.memorylab adb shell am start -W -n com.android.memorylab/.MainActivity
۲. شروع ردیابی و راهاندازی توالی
ردگیری ۴۰ ثانیهای را شروع میکنیم و رویدادهای حافظه را بااستفاده از فرمانهای am
broadcast راهاندازی میکنیم.
شروع ردیابی:
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راهاندازی توالی (این فرمانها را در پایانه میزبان خود درحالیکه ردیابی درحال اجرا است، با رعایت زمانبندی پیشنهادی اجرا کنید):
# 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جایگزین (ابزار 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 باز کنید.
قطعههای کلیدی در Perfetto
قبلاز تجزیهوتحلیل خط زمان، این قطعههای ضروری را برای فرایند
com.android.memorylab پیدا کنید:
mem.rss.anon(RSS ناشناس): در بخش حافظه پردازش یافت میشود. این قطعه حافظه فیزیکی (RAM) اختصاصدادهشده به فرایند توسط سیستمعامل را اندازهگیری میکند. این مقدار نشاندهنده ردپای حافظه واقعی است.Heap size (KB): همچنین در بخش حافظه. این شمارنده مختص Dalvik/ART است که فضای نشانی مجازی رزروشده برای پشته Java را نشان میدهد. این مقدار نشاندهنده حد پشته داخلی ماشین مجازی است که با اختصاص یافتن اشیا و اجرای GC نوسان میکند.HeapTaskDaemon: در فهرست رشتههای زیر فرایند پیدا شد. این رشته پسزمینهای است که در آن ART Garbage Collector بیشتر کار خود را انجام میدهد. فعالیت در اینجا نشاندهنده مجوزهای فعال GC است.- Continuous Allocation Dumps (heapprofd): بهصورت برشهای رنگی درطول خط زمان بالا نشان داده میشود. هر برش نشاندهنده یک مدت زمان است. با کلیک کردن روی یک برش یا انتخاب کردن یک بازه زمانی میتوانید Flamegraph (در قاب پایین) را برای com.android.art (اختصاصهای Java) یا libc.malloc (اختصاصهای Native) بررسی کنید تا ببینید در آن دوره چه چیزی اختصاص داده شده است.
تحلیل فاز زمانی
بیایید ردگیری را بهترتیب زمانی مرور کنیم تا ببینیم این قطعهها چگونه درطول هر فاز تمرین با یکدیگر تعامل دارند.
مرحله ۱: خط مبنا (۰ تا ۵ ثانیه)
- چه اتفاقی میافتد: برنامه غیرفعال است و منتظر فرمانها است.
- وضعیت قطعه:
mem.rss.anon: خط صاف در خط مبنا (معمولاً حدود ۶۰ تا ۸۰ مگابایت بسته به دستگاه).-
Heap size (KB): خط صاف، مطابق با تخصیص اولیه پشته جاوا. -
HeapTaskDaemon: غیرفعال (هیچ برشی اجرا را نشان نمیدهد). - Allocation Dumps: تخصیصهای خط پایه حداقلی را نشان میدهد.

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

فاز ۳: تخصیص جاوا پایدار (۱۵ ثانیه - ۲۰ ثانیه)
- چه اتفاقی میافتد: ۱۰ مگابایت از اشیای Java را اختصاص میدهیم و مرجعی
به آنها در
mJavaAllocationsنگه میداریم. - وضعیت قطعه:
Heap size (KB): خط مبنای دندانهارهای تقریباً ۱۰ مگابایت افزایش مییابد.mem.rss.anon: Steps up by approximately 10MB, as the OS must back this persistent allocation with new physical pages.- رونوشتهای تخصیص پشته جاوا: با انتخاب برشها در این قطعه، تخصیصهای پشته com.android.art نشان داده میشود.
- تخلیههای تخصیص (نمودار شعلهای): بررسی توده com.android.art برای تخلیه انجامشده در این پنجره، مسیر تخصیص جدیدی را از
MainActivity.allocateJavaنشان میدهد که به اندازه حفظشده کمک میکند.
نمونه تخصیصی را انتخاب کنید که مدت آن با افزایش ۱۰ مگابایتی تخصیص پایدار همپوشانی داشته باشد. باید ببینید که پشتههای فراخوانی تخصیص به دو سایت مختلف واگرا میشوند، یکی مسئول همان ریزش تخصیص کوتاهمدت است که قبلاً دیدیم، و دیگری برای تخصیص بلندمدت جدید است.

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

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

پایش کردن OOMهای گذشته (ApplicationExitInfo)
گرفتن «بستن ناشی از کمبود حافظه» درحین وقوع برای اشکالزدایی فعال عالی است، اما برای دورسنجی میدانی، میتوانید از ApplicationExitInfo API استفاده کنید. این کار به برنامهتان امکان میدهد
دلیل پایان یافتن آن در جلسه قبلی را پیدا کند.
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
}
}
روالهای مطلوب
- ابتدا خط مبنا: همیشه پساز مقداردهی اولیه برنامه اما قبلاز انجام کنشی که درحال آزمایش آن هستید، یک رونوشت پشته «خط مبنا» بگیرید.
- استفاده از صفحه نشت فعالیت AHAT: AHAT شامل یک صفحه اختصاصی نشت فعالیت است که بهطور خودکار نمونههای فعالیتی را که ازبین رفتهاند اما هنوز در حافظه نگهداری میشوند شناسایی میکند. این روش اغلب سریعترین راه برای پیدا کردن نشتیهای رایج است.
- بررسی مسیر به ریشههای GC: برای هر شیء نشتشده، از نمای مسیر از ریشه در AHAT استفاده کنید تا دقیقاً بفهمید کدام مرجع آن را زنده نگه میدارد (برای مثال، یک فیلد ایستا، یک رشته طولانیمدت، یا یک شنونده ثبتشده).
← ابزارها | ↑ بالا | نگاشتهای بیتی →