رشته‌ها و حافظه

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

هزینه رشته

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

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

حافظه پشته و mmap

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

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

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

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

۱. راه‌اندازی و وضعیت پایه

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

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

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

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

روی دکمه ایجاد ۱۰۰ رشته تک‌ضرب بزنید. برنامه ۱۰۰ رشته جدید ایجاد خواهد کرد. To ensure the stack memory is actually paged into physical RAM (so we can see it in our tools), each thread allocates and writes to a small 10KB array on its stack before it begins waiting.

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

adb shell dumpsys meminfo -s com.android.memorylab

نتایج:

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

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

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

۳. مشاهده رشته‌ها در Perfetto

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

نماگرفتی از میانای کاربری Perfetto که یک قطعه شمارنده را با تعداد رشته‌های فعال و رشته‌هایی که در برنامه memorylab شروع و خارج می‌شوند نشان می‌دهد

PerfettoSQL برای تجزیه‌وتحلیل رشته

می‌توانید ردگیری را جستجو کنید تا دقیقاً تعداد رشته‌های ایجادشده توسط برنامه را بشمارید:

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-%';

از پُرسمانی مثل این می‌توان برای تولید قطعه اشکال‌زدایی با شمارنده استفاده کرد، همان‌طور که در نماگرفت نشان داده شده است.


← کد برنامه | ↑ بالا | منطقه →