איתור ותיקון דליפות זיכרון באפליקציות iOS ו-Android
האפליקציה קורסת ב-iOS לאחר 20 מעברים של מסך המפה — ה-heap מציג 240 MB רק מ-MapViewController. יומן הקריסה מכיל Terminated due to memory pressure. Instruments → Allocations מראה גידול של כ-12 MB בכל פתיחה, ו-12 MB אלה לעולם לא משוחררים. זו לא השערה אלא דליפת זיכרון מדויקת.
כל הקפאה כזו מפחיתה את ה-LTV של המשתמש: לפי App Annie, דליפות זיכרון מעלות את שיעור הסרת האפליקציה ב-15-20% בשבוע הראשון. תיקון דליפות לא רק חוסך מהאפליקציה קריסות אלא גם חוסך תקציב: עלות תיקון דליפה אחת היא בסדר גודל נמוך יותר מהפסדים כתוצאה מנטישת משתמשים. למהנדסים שלנו עם ניסיון של 7+ שנים יש מעל 30 פרויקטים של אופטימיזציית זיכרון שהושלמו. אנו עוזרים לאתר ולחסל דליפות עם תוצאות מובטחות.
מדוע דליפות זיכרון קריטיות לביצועים
דליפות זיכרון גורמות לגידול הדרגתי ב-RAM, מה שמוביל להאטת CPU, להפסקות GC תכופות ב-Android, ולזמן תגובה מוגבר. בסופו של דבר — קריסה או הקפאה. ב-iOS, בעומס RAM של 80% המערכת מתחילה להרוג תהליכי רקע, וב-90% היא הורגת את האפליקציה. ב-Android — OutOfMemoryError. משתמשים מוחקים אפליקציות כאלה תוך ימים. סטטיסטיקות מראות ש-68% מהמשתמשים נתקלו בבעיות זיכרון, ו-25% מסירים את האפליקציה מיד לאחר שתי קריסות.
סוגים נפוצים של דליפות זיכרון ב-iOS ו-Android
מחזורי שמירה (Retain Cycles) ב-iOS
ARC סופר הפניות חזקות. אם A מחזיק את B ו-B מחזיק את A — אף אחד לא יגיע לספירת אפס ולעולם לא ישוחרר. דפוסים נפוצים:
סגירה (Closure) ללא [weak self]:
// УТЕЧКА
viewModel.onDataLoaded = {
self.tableView.reloadData()
}
// ПРАВИЛЬНО
viewModel.onDataLoaded = { [weak self] in
self?.tableView.reloadData()
}טיימר:
// УТЕЧКА — Timer удерживает target сильно
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(tick), userInfo: nil, repeats: true)כאשר ה-ViewController נסגר, הטיימר ממשיך לעבוד ומחזיק את ה-ViewController. פתרון — // УТЕЧКА viewModel.onDataLoaded = { self.tableView.reloadData() } // ПРАВИЛЬНО viewModel.onDataLoaded = { [weak self] in self?.tableView.reloadData() } עם // УТЕЧКА — Timer удерживает target сильно timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(tick), userInfo: nil, repeats: true) ו-Timer.scheduledTimer(withTimeInterval:repeats:block:) ב-[weak self].
Delegate ללא timer.invalidate():
// УТЕЧКА
protocol DataDelegate: AnyObject {
func didLoad()
}
class DataService {
var delegate: DataDelegate? // должен быть weak!
}השתמש ב-deinit.
דליפות ב-Android
דליפת Context היא הנפוצה ביותר: Context של Activity מאוחסן בסינגלטון. ה-Activity לא משוחרר כל עוד ה-Repository חי. השתמש תמיד ב-weak.
מחלקה פנימית אנונימית + Handler:
private val handler = Handler(Looper.getMainLooper()) handler.postDelayed({ updateUI() }, 5000) פתרון: // УТЕЧКА protocol DataDelegate: AnyObject { func didLoad() } class DataService { var delegate: DataDelegate? // должен быть weak! } או weak var delegate: DataDelegate?.
מאזיני LiveData ללא removeObserver: הרשמה עם applicationContext כאשר private val handler = Handler(Looper.getMainLooper()) handler.postDelayed({ updateUI() }, 5000) הוא Fragment במקום WeakReference<MyActivity> מובילה להצטברות מאזינים בכל יצירה מחדש של ה-View. השתמש תמיד ב-lifecycleScope.launch { delay(5000); updateUI() }.
כלים לאיתור דליפות זיכרון
| כלי | פלטפורמה | סוגי דליפות | תכונות |
|---|---|---|---|
| LeakCanary | Android | Activity, Fragment, View | ניטור אוטומטי, עץ שמירה |
| Instruments Leaks | iOS | מחזורי שמירה | גרף אובייקטים, אינטגרציה עם Allocations |
| Memory Profiler | Android | כל האובייקטים | Heap dump, נתיב לשורש GC |
| Instruments Alloc | iOS | דליפות לוגיות | ניתוח דורות, גידול heap |
| שיטה | מורכבות | יעילות | זמן יישום |
|---|---|---|---|
| בדיקת קוד ידנית | בינונית | 60% | 2-3 ימים |
| LeakCanary/Instruments | נמוכה | 95% | יום אחד |
| בדיקות CI | גבוהה | 99% | 3-5 ימים |
Instruments Leaks מזהה מחזורי שמירה פי 5 מהר יותר מאשר בדיקת קוד ידנית. LeakCanary מוצא דליפות פי 3 מהר יותר מאשר ניתוח heap dump ידני.
פרטים: LeakCanary ו-Instruments.
מקרה בוחן: דליפת RxJava Disposable
לאחרונה הגיע אלינו לקוח — מפתח Flutter שעבר ל-Android. לאפליקציה שלו הייתה דליפה דרך liveData.observe(this, ...). המינוי נוצר ב-this, וה-viewLifecycleOwner מעולם לא נשמר. בכל סיבוב מסך, נוצר Observer חדש, בעוד שהישן המשיך לפעול. אחרי 10 סיבובים — 10 תהליכים פעילים. LeakCanary מצא את זה תוך 2 דקות: Activity שמור דרך viewLifecycleOwner. פתרון: השתמש ב-Observable.interval, הוסף את כל המינויים, קרא ל-onCreate ב-Disposable או ב-Observable → Observer → Activity reference.
התהליך שלנו ולוחות זמנים
- ביקורת קוד בסיסית (1-2 ימים)
- התקנת LeakCanary / Instruments (0.5 יום)
- הרצת תרחישי בדיקה (0.5 יום)
- ניתוח heap dumps (יום אחד)
- תיקון דליפות (2-5 ימים תלוי במורכבות)
- הוספת לוגים ב-deinit/onDestroy (0.5 יום)
- בדיקות רגרסיה (יום אחד)
לוחות זמנים: אבחון — 1-3 ימים, תיקון — 2-7 ימים.
מה כלול בשירות
אנו מספקים דוח מלא עם דיאגרמות heap dump, הוראות ליישום דפוסי הגנה, והמלצות ארכיטקטורה למניעת דליפות עתידיות. במידת הצורך, אנו מתקינים בדיקות CI (LeakCanary ב-debug, בדיקות מחזורי שמירה קפדניות). כתוצאה מכך, אתה מקבל אפליקציה יציבה עם הפחתה אופיינית של 30-50% בשימוש ב-RAM.
טעויות נפוצות בחיפוש דליפות לבד
- שימוש ב-Instruments Leaks רק לתרחיש אחד — דליפות עשויות להופיע רק ברצף פעולות ספציפי.
- התעלמות מדליפות בספריות צד שלישי — הבעיה היא לעתים קרובות לא בקוד שלך אלא בתלות.
- שכחת לבדוק deinit/onDestroy בתת-מחלקות של ViewController/Activity — השגיאה צצה חודש לאחר מכן.
דליפות זיכרון אינן קטלניות, אך הן דורשות גישה שיטתית. אנו מציעים מחזור מלא מאבחון ועד יישום אמצעי הגנה. צור קשר לייעוץ על הפרויקט שלך. הזמן ביקורת זיכרון עוד היום.







