مدیریت خودکار حافظه از طریق زبالهروبی مکانیسم کلیدی پلتفرم Android است که بر ماشین مجازی ART استوار دارد. به گزارش Google Android Documentation, 2026، زبالهروب توسعهدهنده را از مدیریت دستی حافظه آزاد میکند و شیئهایی را که دیگر ارجاعی ندارند به صورت خودکار حذف میکند. بدون GC هر تخصیص شیئ نیازمند فراخوانی صریح free یا delete بود که در اکوسیستم Java با میلیونها شیئ در ثانیه از لحاظ فیزیکی غیرممکن است.
نکات کلیدی
Garbage Collection (GC) — یک فرآیند خودکار برای کشف و آزادسازی حافظهای است که توسط شیئهایی اشغال شده که دیگر توسط برنامه استفاده نمیشوند. در زمینه توسعه موبایل، GC در پلتفرم Android از طریق ماشین مجازی ART و همچنین در Java Virtual Machine استاندارد اعمال میشود.
بر خلاف زبانهای با مدیریت دستی حافظه (C, C++) که برنامهنویس مجبور است صریحاً free یا delete را فراخوان کند، GC کاملاً وظیفه پیگیری چرخه عمر شیئها را بر عهده میگیرد. توسعهدهنده شیئهای جدید را از طریق عامل new ایجاد میکند و زبالهروب لحظهای را که شیئ غیرقابل دسترس میشود را تعیین میکند — یعنی هیچ ارجاع فعالی به آن باقی نمانده است.
اصلیترین متریک کارایی GC — زمان مکث (pause time) و انتقال دهی (throughput). مکث مدتی است که اجرای اپلیکیشن برای انجام جمعآوری متوقف میشود. در محیط موبایل، مکاث بیشتر از 8–16 میلیثانیه به صورت فرمهای افتاده (jank) قابل توجه هستند.
استناد به Google I/O 2019، ART در Android 10 مکاث معمولی GC را به 2–4 ms کاهش داد که نسبت به Dalvik در Android 4.4 ۷۰ درصد کمتر است. با این حال، کار نادرست با حافظه — تخصیص مکرر شیئ در حلقهها، ایجاد نمونههای موقت بدون نیاز — علت اصلی مشکلات کارایی باقی میماند.
همه پیادهسازیهای GC در Java و Android بر چند الگوریتم بنیادین استوار هستند که برای دستیابی به تعادل بین زمان مکث و کاملیت پاکسازی ترکیب میشوند. درک این الگوریتمها برای نوشتن کد مناسب برای GC ضروری است.
Mark-and-Sweep — سادهترین الگوریتم که در دو مرحله کار میکند. در مرحله Mark، زبالهروب گراف شیئها را از ارجاعات ریشه (root set) — متغیرهای محلی، فیلدهای استاتیک، پیشکهای پروندهها — پیمایش میکند. هر شیئ قابل دسترس با علمت live علامتگذاری میشود. در مرحله Sweep، زبالهروب از کل پیشکنبه عبور میکند و حافظه شیئهای علامتگذاری نشده را آزاد میکند.
نقص — تکه تکه شدن حافظه: پس از Sweep، بخشهای آزاد با بخشهای اشغالی درهم قرار میگیرند که تخصیص شیئهای بزرگ را دچار مشکل میکند. در سناریوهای موبایل این حریجانی است چون پیشکنبه معمولاً کوچک است (64–512 MB در Android).
Copying Collection پیشکنبه را به دو نیمفضا (semi-spaces) تقسیم میکند. شیئهای فعال از یک نیمفضا به دیگری به صورت متراکم و بدون شکاف کپی میشوند. پس از کپی، نیمفضای قدیمی کاملاً آزاد اعلام میشود. این الگوریتم کاملاً تکه تکه شدن را برطرف میکند، اما دو برابر حافظه نیاز دارد.
در محیطهای موبایل، Copying Collection توسط زبالهروبهای نسلی برای پاکسازی سریع شیئهای جوان استفاده میشود که به صورت آماری زود میمیرند (فرضیه نسل ضعیف).
Generational Collection پیشکنبه را به نسلها تقسیم میکند: Young Generation (شیئهای جوان) و Old Generation (شیئهای پیر که چند بار جمعآوری را تحمل کردهاند). جمعآوری نسل جوان (Minor GC) به صورت مکرر و سریع انجام میشود، چون اکثر شیئها جوان میمیرند. جمعآوری نسل پیر (Major GC یا Full GC) به ندرت اتفاق میافتد اما بیشتر طول میکشد.
// نمایش GC نسلی: شیئهای جوان زود میمیرند
void processItems(List<Item> items) {
List<Result> results = new ArrayList<>(); // تمام متد زندگی میکند
for (Item item : items) {
Result r = new Result(item.getValue()); // فوراً میمیرد
if (r.isValid()) {
process(r); // r زباله میشود
}
}
saveResults(results); // results به Old Gen منتقل میشود
}
در این مثال، شیئهای Result درون حلقه ایجاد میشوند و بلافاصله زباله میشوند — آنها کاندیدای ایدهای برای Young GC هستند. شیئ results بیشتر زندگی میکند و به Old Generation مهاجرت میکند. جداسازی نسلها به Minor GC اجازه میدهد شیئهای جوان را در چند میلیثانیه پاک کند بدون اینکه به پیشکنبه قدیمی دست بزند.
Android راهی از Dalvik VM به ART (Android Runtime) طی کرده است و پیادهسازی GC یکی از تفاوتهای کلیدی بین آنهاست. درک معماری GC در Android به نوشتن کدی کمک میکند که مکاث را در دستگاههای واقعی به حداقل برساند.
| ویژگی | Dalvik (تا 4.4) | ART (5.0+) |
|---|---|---|
| نوع GC | Mark-and-Sweep با Concurrent Mark | نسلی + Concurrent |
| مکث معمولی | 10–30 ms | 2–4 ms |
| تراکم | ندارد (تکه تکه شدن افزایش مییابد) | دارد (در پسزمینه، بدون توقف اپلیکیشن) |
| کامپایل AOT | JIT (Just-In-Time) | AOT + JIT (ترکیبی) |
Dalvik از ترکیب Mark-and-Sweep با فاز concurrent استفاده میکرد. Concurrent Mark به اپلیکیشن اجازه میداد در طول پیمایش گراف شیئها به کار خود ادامه دهد، اما مرحله Sweep نیازمند توقف همه پروندهها (Stop-The-World) بود. در دستگاههای با حافظه کم (512 MB — 1 GB)، مکاث به 30 ms میرسید که باعث کندی قابل توجه در رابط میشد. علاوه بر این، Dalvik پیشکنبه را متراکم نمیکرد، بنابراین پس از کار طولانی، تکه تکه شدن افزایش مییافت و تخصیص شیئهای بزرگ (مانند Bitmap) میتوانست OutOfMemoryError را حتی با حافظه آزاد کافی ایجاد کند.
ART (Android Runtime) یک زبالهروب نسلی با تراکم همزمان معرفی کرد. پیشکنبه به سه منطقه تقسیم میشود: Young، Mature (مشابه Old Generation) و Large Object Space (برای شیئهای بزرگتر از 12 KB). جمعآوری Young Region در اکثر موارد بدون توقف پروندهها به صورت موازی انجام میشود. در Android 10+ ، Concurrent Copying ظاهر شد — تراکم در پسزمینه بدون Stop-The-World انجام میشود.
به لطف معماری ART، مکاث معمولی GC به 2–4 ms کاهش یافت و در سناریوهایی که شیئهای جوان غالب هستند — به 0.5–1 ms. این به دستگاههای Android امکان داد حتی در کار فعال با حافظه 60 FPS پایدار تامین کنند.
در اکوسیستم Java چندین پیادهسازی GC وجود دارد که هر کدام منحنصر به فرد نیروی کارایی خود را دارند. برای توسعه Android انتخاب به ART محدود است، اما دانش Java GC در نوشتن بخش سرور اپلیکیشنهای موبایل و توسعه با Kotlin Multiplatform مفید است.
Serial GC — زبالهروب تک پروندهای با توقف کامل اپلیکیشن (Stop-The-World). هر عملیات Mark، Sweep و Compact توسط یک پرونده انجام میشود. کارایی پایین است — برای سرورهای موبایل مناسب نیست. فقط برای اپلیکیشنهای کوچک با پیشکنبه تا 100 MB مناسب است.
Parallel GC (همچنین به عنوان Throughput Collector شناخته میشود) از چند پرونده برای همه فازهای جمعآوری استفاده میکند. بر حداکثر انتقال دهی (throughput) متمرکز است — زمان صرف شده در GC نسبت به زمان کار اپلیکیشن را مینیمیزم میکند. از طریق علمت -XX:+UseParallelGC در JVM فعال میشود.
G1 (Garbage-First) GC — زبالهروب پیشفرض در Java 9+. پیشکنبه به مناطق 1–32 MB تقسیم میشود. G1 زمان مکث را پیشبینی میکند و تلاش میکند در لیمیت تعیین شده (پیشفرض 200 ms) قرار گیرد. اولویت: ابتدا مناطقی پاک میشوند که بیشترین حجم زباله را دارند (از اینجا نام G1). G1 برای سرورهای با پیشکنبه بزرگ (4–64 GB) و مکاث قابل پیشبینی مناسب است.
// فعالسازی G1 GC با مکث هدف 100 ms
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar app.jar
public class MemoryMonitor {
private static final long THRESHOLD = 512 * 1024 * 1024; // 512 MB
public void checkHeapUsage() {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
if (used > THRESHOLD) {
System.out.println("حد استفاده از پیشکنبه تجاوز شد: " + used);
System.out.println("کاهش تخصیصها را در نظر بگیرید");
}
}
}
نظارت بر پیشکنبه از طریق Runtime به تشخیص زودهنگام نشت حافظه کمک میکند. اگر used در کار پایدار از 80% حداکثر پیشکنبه تجاوز کند — این نشانهای از نشت حافظه یا مصرف بیش از حد حافظه توسط اپلیکیشن است.
حتی ART GC مودرن همه مشکلات را حل نمیکند — استفاده نادرست از حافظه علت اصلی jank و ANR (Application Not Responding) باقی میماند. بیایید سناریوهای اصلی و روشهای بهینهسازی را بررسی کنیم.
GC Pauses — توقف پروندههای اپلیکیشن در طول جمعآوری. در صفحه نمایش، این به صورت فرمهای افتاده ظاهر میشود زمانی که زمان بین دو فرم از 16.6 ms (60 FPS) تجاوز کند. اگر GC 30 ms طول بکشد، به جای دو فرم، فقط یک فرم رسم میشود — کاربر کندی را در رابط میبیند.
دلایل اصلی مکاث طولانی: تعداد زیاد شیئهای زنده در Old Generation، تکه تکه شدن پیشکنبه، Full GC مکرر. برای تشخیص از Android Studio Profiler و systrace استفاده میشود.
قاعده اصلی کد مناسب برای GC — کاهش تعداد شیئهای تخصیص شده. هر شیئ جدید نه تنها نیازمند تخصیص حافظه است، بلکه جمعآوری بعدی را نیز نیاز دارد. حتی اگر GC سریع باشد، 1000 تخصیص اضافی در ثانیه 1000 بررسی برای زبالهروب ایجاد میکند.
نشت حافظه زمانی رخ میدهد که یک شیئ قابل دسترس باقی بماند، در حالی که دیگر نیاز نیست. GC نمیتواند چنین شیئی را حذف کند و حافظه تدریجاً تمام میشود. علل معمولی: اشتراکهای پاک نشده، ارجاعات استاتیک به Activity، کلاسهای ناشناس که کنتکست خارجی را گرفتهاند و Cursor/InputStream بسته نشده.
// نشت حافظه: کلاس ناشناس ارجاعی به Activity نگه میدارد
public void startTask() {
new Thread(new Runnable() { // ضمناً this (Activity) را نگه میدارد
@Override
public void run() {
// عملیات طولانی...
System.out.println("انجام شد");
}
}).start();
}
// رفع: کلاس تودره استاتیک + WeakReference
private static class TaskRunnable implements Runnable {
private WeakReference<Activity> activityRef;
TaskRunnable(Activity activity) {
this.activityRef = new WeakReference<>(activity);
}
@Override
public void run() {
Activity act = activityRef.get();
if (act != null) {
// کار ایمن با Activity
}
}
}
در این مثال، Runnable ناشناس یک ارجاع ضمنی به Activity گرفته است. تا زمانی که پرونده زنده است — Activity نمیتواند توسط GC جمع شود، حتی اگر کاربر صفحه را بسته باشد. رفع WeakReference + static class این زنجیره را میشکند و به Activity اجازه بازیابی میدهد.
سوالات متداول
GC در Android (ART) — یک زبالهروب نسلی با تراکم همزمان است که برای دستگاههای موبایل با حافظه محدود بهینه شده است. Java GC (G1, ZGC) زبالهروبهای سروری با پیشکنبههای بزرگ و مکاث قابل پیشبینی هستند. ART GC از فلاگهای JVM استفاده نمیکند — تمام تنظیمات به صورت خودکار در سطح سیستمعامل انجام میشود.
Stop-The-World — لحظهای است که زبالهروب تمام پروندههای اپلیکیشن را متوقف میکند تا به صورت ایمن از گراف شیئها عبور کند یا حافظه را آزاد کند. هر چقدر STW طولانیتر باشد، jank بیشتر قابل توجه است. ART زمان معمولی STW را به 2–4 ms به لطف معماری نسلی کاهش داده است.
از Android Studio Memory Profiler استفاده کنید — رشد پیشکنبه، تعداد تخصیصها را نشان میدهد و امکان Heap Dump را فراهم میکند. برای تحلیل عمیق، از LeakCanary استفاده کنید — این کتابخانه به صورت خودکار نشتها را تشخیص میدهد و زنجیره ارجاعاتی را که مانع جمعآوری GC میشود نشان میدهد.
Full GC — جمعآوری کامل همه نسلهای پیشکنبه، شامل Old Generation. در اپلیکیشنهای موبایل، Full GC میتواند 50–200 ms طول بکشد و باعث jank قابل توجه یا ANR شود. علل اصلی: تکه تکه شدن پیشکنبه، نشت حافظه، تجاوز حد Old Generation.
Kotlin کوروتینهایی با همزمانی ساختاریافته فراهم میکند — لغو scope به صورت خودکار تمام کوروتینهای فرزند را لغو میکند و از نشت حافظه جلوگیری میکند. همچنین در Kotlin دلگات lazy برای مقداردهی تنبل و عاملگرهای حوزه دید وجود دارند که تعداد شیئهای موقت را کاهش میدهند.
نتایج
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید