نخ‌ها و حافظه

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

هزینه یک نخ

وقتی یک نخ در اندروید ایجاد می‌شود، دو جزء اصلی حافظه را مصرف می‌کنند:

  1. پشته بومی : کتابخانه pthread یک پشته برای نخ اختصاص می‌دهد تا متغیرهای محلی را ذخیره کرده و فریم‌ها را فراخوانی کند. در اندروید، این مقدار معمولاً به طور پیش‌فرض ۱ مگابایت است.
  2. ساختارهای داخلی JVM/Kernel : ART و هسته لینوکس باید ساختارهای داده‌ای (مانند اشیاء pthread_internal_t ​​و java.lang.Thread ) را برای ردیابی و مدیریت نخ اختصاص دهند.

حافظه پشته و mmap

مهم است بدانید که اندروید ۱ مگابایت از فضای پشته بومی را با استفاده از mmap و با استفاده از پرچم‌های MAP_ANONYMOUS | MAP_NORESERVE اختصاص می‌دهد.

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

تمرین عملی: نشت نخ‌ها

ما از برنامه نمونه MemoryLab استفاده خواهیم کرد تا مشاهده کنیم وقتی یک برنامه صدها نخ بیکار ایجاد می‌کند، چه اتفاقی می‌افتد.

۱. شروع و شروع اولیه

برنامه را اجرا کنید و یک خوانش حافظه پایه انجام دهید:

adb shell am start -W -n com.android.memorylab/.MainActivity
adb shell dumpsys meminfo -s com.android.memorylab

به ردیف‌های Stack و Native Heap در خروجی نگاه کنید. احتمالاً کوچک خواهند بود.

۲. ۱۰۰ رشته ایجاد کنید

روی دکمه‌ی «ایجاد ۱۰۰ رشته» (Create 100 Threads ) ضربه بزنید. برنامه ۱۰۰ رشته‌ی جدید ایجاد خواهد کرد. برای اطمینان از اینکه حافظه‌ی پشته واقعاً به رم فیزیکی منتقل می‌شود (تا بتوانیم آن را در ابزارهای خود ببینیم)، هر رشته قبل از شروع انتظار، یک آرایه‌ی کوچک ۱۰ کیلوبایتی را روی پشته‌ی خود اختصاص داده و در آن می‌نویسد.

دستور meminfo را دوباره در حالی که نخ‌ها فعال هستند اجرا کنید:

adb shell dumpsys meminfo -s com.android.memorylab

نتایج:

خروجی جدید را با خروجی پایه خود مقایسه کنید. در حالی که نخ‌ها فعال هستند، متوجه افزایش قابل توجهی خواهید شد:

  • Stack ): به اندازه یک مگابایت یا بیشتر افزایش می‌یابد. این نشان دهنده صفحات فیزیکی اختصاص داده شده برای ۱۰۰ پشته نخ (هر کدام حداقل ۱۰ کیلوبایت) است.
  • Native Heap / Private Other : به دلیل ساختارهای داخلی pthread که توسط سیستم برای هر نخ اختصاص داده شده است، افزایش یافته است.

برای پاک‌سازی، روی دکمه‌ی «نابود کردن همه موضوعات» (Destroy All Threads) ضربه بزنید.

۳. مشاهده‌ی موضوعات در Perfetto

Perfetto برای ردیابی چرخه حیات نخ‌ها و شمارش آنها در طول زمان بسیار عالی است.

تصویری از رابط کاربری Perfetto که یک شمارنده با تعداد نخ‌های فعال و نخ‌های شروع و پایان یافته در برنامه memorylab را نشان می‌دهد.

PerfettoSQL برای تحلیل رشته‌ها

می‌توانید با استفاده از کوئری trace تعداد دقیق نخ‌های ایجاد شده توسط برنامه را بشمارید:

SELECT p.name AS process_name, COUNT(t.id) as thread_count
FROM thread t JOIN process p USING (upid)
WHERE p.name = 'com.android.memorylab' AND t.name LIKE 'LeakedThread-%';

از چنین کوئری می‌توان برای تولید یک مسیر اشکال‌زدایی با یک شمارنده، همانطور که در تصویر نشان داده شده است، استفاده کرد.


← کد برنامه | ↑ بالا | موقعیت مکانی →