Διαρροή μνήμης — ένα από τα πιο ύπουλα προβλήματα στην ανάπτυξη κινητών εφαρμογών. Η μνήμη της εφαρμογής αυξάνεται ασταμάτητα έως ότου φτάσει το όριο που έχει ορίσει το λειτουργικό σύστημα, οπότε ακολουθεί OutOfMemoryError ή αναγκαστικός τερματισμός. Σύμφωνα με τη Square Engineering, περίπου το 40% των Android εφαρμογών έχει τουλάχιστον μία διαρροή μνήμης που μπορεί να ανιχνευθεί μόνο με προφίλ. Ας αναλύσουμε τις αιτίες και τις μεθόδους πρόληψης της αύξησης μνήμης.
Κύρια σημεία
Διαρροή μνήμης (memory leak) — κατάσταση κατά την οποία ένα αντικείμενο που δεν χρειάζεται πλέον η εφαρμογή συνεχίζει να παραμένει στο heap επειδή υπάρχει ενεργή αναφορά από το root set (GC Root). Ο συλλέκτης σκουπιδιών θεωρεί ένα τέτοιο αντικείμενο ζωντανό και δεν το διαγράφει.
Φούσκωμα μνήμης (memory bloat) — ευρύτερο πρόβλημα όταν η εφαρμογή καταναλώνει περισσότερη μνήμη από όση χρειάζεται για την εκτέλεση των τρεχουσών εργασιών. Αιτίες: υπερβολική προσωρινή αποθήκευση, διπλασιασμός αντικειμένων, μη βέλτιστες δομές δεδομένων και κατακερματισμός heap.
Στο Android κάθε εφαρμογή λαμβάνει περιορισμένο heap (συνήθως 64-512 MB ανάλογα με τη συσκευή και την έκδοση OS). Στο iOS ο περιορισμός είναι λιγότερο αυστηρός, αλλά το σύστημα στέλνει προειδοποίηση μνήμης όταν πλησιάζει το όριο.
| Χαρακτηριστικό | Android | iOS |
|---|---|---|
| Όριο heap | 64-512 MB (ανάλογα με τη συσκευή) | Σιωπηρό (σύστημα) |
| Συλλογή σκουπιδιών | ART (Concurrent, Compact) | ARC (Automatic Reference Counting) |
| Μηχανισμός διαρροής | Αναφορές GC Root | Retain cycles (ισχυροί κύκλοι αναφορών) |
| Αποτέλεσμα | OutOfMemoryError | Προειδοποίηση μνήμης → τερματισμός |
Σύμφωνα με το Facebook Engineering Blog, οι διαρροές μνήμης είναι η αιτία ~15% των αναφορών crash σε εφαρμογές κινητών. Στο Android προστίθενται ANR λόγω συχνών παύσεων GC όταν υπάρχει έλλειψη μνήμης.
Στατική αναφορά σε Activity — κλασική διαρροή Android. Αν ένα στατικό πεδίο ή singleton αποθηκεύει αναφορά σε Activity, αυτή δεν θα συλλεγεί από τον GC ακόμα και μετά το finish(), όσο το singleton ζει. Το Activity είναι βαρύ αντικείμενο που περιέχει ιεραρχία View, πόρους και Context.
object LeakHolder {
var activityRef: Activity ?= null // διαρροή: στατική αναφορά σε Activity
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle ?= null) {
super.onCreate(savedInstanceState)
LeakHolder.activityRef = this // ❌ Η MainActivity δεν θα συλλεγεί ποτέ από τον GC
}
}
Ανώνυμες κλάσεις και lambdas — σιωπηρά διατηρούν αναφορά στην εξωτερική κλάση. Αν ένα Runnable ή Callback μεταβιβαστεί σε εξωτερική υπηρεσία και η Activity καταστραφεί, το αντικείμενο της ανώνυμης κλάσης εξακολουθεί να κρέμεται στην ουρά και δεν επιτρέπει στην Activity να συλλεγεί από τον GC.
Στο iOS το κύριο πρόβλημα είναι οι retain cycles: δύο αντικείμενα διατηρούν ισχυρές αναφορές το ένα στο άλλο και η ARC δεν μπορεί να μηδενίσει τον μετρητή αναφορών για κανένα. Τυπική περίπτωση: ένα closure που συλλαμβάνει το self ισχυρά, και το self που κρατά αναφορά στο closure.
LeakCanary — βιβλιοθήκη από τη Square για αυτόματη ανίχνευση διαρροών στο Android. Μετά την καταστροφή Activity ή Fragment ελέγχει αν το αντικείμενο συλλέχθηκε από τον GC. Αν όχι — κάνει heap dump και εμφανίζει το ίχνος διαρροής.
// LeakCanary 2.x — αυτόματη ενσωμάτωση μέσω Application
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// Το LeakCanary εγκαθίσταται αυτόματα σε debug build
// μέσω ContentProvider — ρύθμιση με μηδενικό κώδικα
}
}
// Αναγκαστική κλήση ελέγχου
AppWatcher.objectWatcher.watch(watchedObject, "leak description")
Android Studio Profiler — το ενσωματωμένο εργαλείο για παρακολούθηση μνήμης σε πραγματικό χρόνο. Επιτρέπει εγγραφή heap dump, εύρεση ύποπτων αντικειμένων (Retained Size > 1 MB) και παρακολούθηση της διαδρομής GC root προς κάθε αντικείμενο.
Για iOS χρησιμοποιήστε το Xcode Memory Graph Debugger. Απεικονίζει το γράφημα αντικειμένων στη μνήμη, δείχνει retain cycles και επιτρέπει άμεσο εντοπισμό κυκλικών αναφορών. Επίσης διαθέσιμο είναι το Instruments > Allocations για μακροπρόθεσμη παρακολούθηση.
WeakReference — ο βασικός μηχανισμός για αναφορές που δεν πρέπει να εμποδίζουν τη συλλογή σκουπιδιών. Αν ο GC αποφασίσει να διαγράψει το αντικείμενο, η WeakReference επιστρέφει null. Χρησιμοποιείται για callbacks, listeners και αναφορές σε UI components από νήματα παρασκηνίου.
Lifecycle-aware components — η αρχιτεκτονική προσέγγιση που υλοποιείται στο Android Jetpack (Lifecycle, LiveData, Flow, coroutines). Οι συνδρομές ακυρώνονται αυτόματα στο onDestroy, εξαλείφοντας την κύρια κατηγορία διαρροών.
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<List<User>>()
val data: LiveData<List<User>> get() = _data
fun loadData() {
viewModelScope.launch {
val result = repository.fetchData()
_data.postValue(result)
// το coroutine ακυρώνεται αυτόματα στο onCleared()
}
}
}
viewModelScope και lifecycleScope — ενσωματωμένα CoroutineScope στο Android που ακυρώνονται στο αντίστοιχο γεγονός κύκλου ζωής. Αυτό εξαλείφει τις διαρροές μέσω coroutines — το πιο συνηθισμένο σενάριο στη σύγχρονη ανάπτυξη Android.
Memory Profiler in Android Studio — το κύριο εργαλείο για παρακολούθηση heap. Εμφανίζει live allocations, στιγμιότυπα heap, αριθμό αντικειμένων ανά τύπο. Επιτρέπει εγγραφή dump και ανάλυσή του στο MAT (Memory Analyzer Tool) για εύρεση ύποπτων αντικειμένων.
Eclipse MAT — επιτραπέζιος αναλυτής heap dump. Μετά τη φόρτωση του αρχείου HPROF από το Android Studio, το MAT χτίζει το dominator tree, δείχνει το retain size κάθε αντικειμένου και προσφέρει αυτόματη ανάλυση ύποπτων διαρροών μέσω του Leak Suspects Report.
Xcode Memory Graph — οπτικός εντοπιστής σφαλμάτων για retain cycles. Με το πάτημα του κουμπιού Memory Graph Debugger, το Xcode σταματά την εφαρμογή, χτίζει πλήρες γράφημα αντικειμένων στη μνήμη και επισημαίνει τα retain cycles με κόκκινο χρώμα.
| Εργαλείο | Πλατφόρμα | Χαρακτηριστικό |
|---|---|---|
| LeakCanary | Android | Αυτόματη ανίχνευση διαρροής μετά από destroy |
| Memory Profiler | Android Studio | Heap dump + live allocations |
| Eclipse MAT | Android | Dominator tree, Leak Suspects Report |
| Memory Graph | iOS (Xcode) | Οπτικοποιητής retain cycles |
Σύμφωνα με το Google I/O 2023, οι εφαρμογές που χρησιμοποιούν LeakCanary σε debug builds μειώνουν τον αριθμό των crash που σχετίζονται με μνήμη κατά 30-50% τους πρώτους 2 μήνες μετά την εφαρμογή. Συνιστάται η προσθήκη του LeakCanary στη φάση ενσωμάτωσης του έργου.
Συχνές ερωτήσεις
Διαρροή — αντικείμενα μη προσβάσιμα από τον κώδικα αλλά μη διαγραμμένα από τον GC λόγω ενεργών αναφορών. Φούσκωμα — η εφαρμογή διατηρεί στη μνήμη αντικείμενα που είναι λογικά απαραίτητα αλλά σε υπερβολική ποσότητα (π.χ. cache 50 MB σε μια εφαρμογή 80 MB). Το φούσκωμα αντιμετωπίζεται αρχιτεκτονικά, η διαρροή — μέσω σωστής διαχείρισης αναφορών.
LeakCanary χρησιμοποιεί ObjectWatcher — μετά το onDestroy() μιας Activity δημιουργεί WeakReference στην Activity και εκκινεί τον GC. Αν μετά από 5 δευτερόλεπτα η WeakReference δεν έχει καθαριστεί, το LeakCanary κάνει heap dump, αναλύει τη συντομότερη αλυσίδα αναφορών από GC Root στο αντικείμενο και εμφανίζει το ακριβές stack διαρροής με όνομα αρχείου και γραμμή κώδικα.
Bitmap καταλαμβάνει μνήμη εκτός του Java heap στην native μνήμη (native heap). Μέγεθος ενός Bitmap = πλάτος × ύψος × 4 bytes (ARGB_8888). Μια φωτογραφία 12 MP (4000×3000) καταλαμβάνει 48 MB. Το Android δεν μπορεί πάντα να ελευθερώσει έγκαιρα τη native μνήμη, που όταν συσσωρεύονται πολλά Bitmaps οδηγεί σε OOM ακόμα και με επαρκή Java heap.
Retain cycle — κατάσταση στο ARC όπου δύο αντικείμενα διατηρούν ισχυρές αναφορές το ένα στο άλλο και ο μετρητής αναφορών δεν φτάνει ποτέ στο μηδέν. Τυπικό παράδειγμα: ViewController με ισχυρή αναφορά σε closure, και το closure συλλαμβάνει ισχυρά το self. Λύση: χρησιμοποιήστε [weak self] ή [unowned self] στα closures.
Το μέγεθος heap εξαρτάται από τη συσκευή και την έκδοση Android. Για παλιές συσκευές (API 15-24) — 64-128 MB. Για σύγχρονες (API 25+) — 256-512 MB. Η ακριβής τιμή μπορεί να ληφθεί μέσω ActivityManager.getMemoryClass(). Για μεγάλες εφαρμογές (παιχνίδια, επεξεργαστές) υπάρχει largeHeap=true στο manifest, που δίνει έως 1 GB.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης