Πώς λειτουργεί ο Low Memory Killer στο Android, και γενικά περί RAM
- Τετάρτη, 26 Μαρτίου 2014 23:10
Το Android όπως έχουμε ξαναπεί, χειρίζεται την μνήμη RAM με "διαφορετικό" τρόπο από ότι οι περισσότεροι έχουν συνηθίσει από τα Windows.
Συγκεκριμένα, κάθε εφαρμογή που ανοίγουμε, μένει στην μνήμη RAΜ, ώστε αν χρειαστεί να την ξαναχρησιμοποιήσουμε, αυτή να ανοίξει στιγμιαία.
Η γεμάτη μνήμη RAM, δεν έχει καμία αρνητική επίδραση στην απόδοση του συστήματός μας, οπότε το να "γεμίζει" η μνήμη RAM δεν είναι ένδειξη προβλήματος.
Επίσης αυτός είναι και ο λόγος που η χρήση ενός task killer, ο οποίος κάνει kill όλες τις εφαρμογές στο παρασκήνιο "άκριτα", έχει αρνητική επίδραση στην συσκευή μας, αντί για θετική.
Τι γίνεται όμως στην περίπτωση που η RAM έχει (καλώς) "γεμίσει", και εμείς θελήσουμε να "ανοίξουμε" μία ακόμα εφαρμογή?
Το Android περιέχει έναν LMK Driver (lowmemorykiller.c) ο οποίος φροντίζει όταν το σύστημα "χρειαστεί" RAM, να ελευθερώνει από εκεί που πρέπει, κλείνοντας μόνο "αυτά που δεν χρειάζονται" από το παρασκήνιο.
Και πώς λοιπόν γνωρίζει ο LMK Driver του Android, ποια εφαρμογή να τερματίσει, όταν το λειτουργικό χρειάζεται περισσότερη RAM, για να ανοίξει κάποια άλλη εφαρμογή?
Αυτό καθορίζεται από την oom (out of memory) priority που ορίζει το Android για κάθε app. Το Android "κρίνει" πόσο σημαντική είναι κάθε εφαρμογή στο παρασκήνιο, και ορίζει αντιστοίχως την μεταβλητή oom_adj (ή το oom_score_adj ? σε νεότερες εκδόσεις).
Όσο μικρότερη η τιμή του oom_adj, τόσο λιγότερες οι πιθανότητες να γίνει kill η αντίστοιχη εφαρμογή από τον LMK.
Στις εφαρμογές που εγκαθιστά ο χρήστης, το oom_adj κυμαίνεται μεταξύ 0 και 16, ενώ οι εφαρμογές συστήματος παίρνουν τιμές μέχρι και -17.
Για παράδειγμα το Android System έχει oom_adj -16, το οποίο προφανώς σημαίνει ότι ο LMK driver δεν θα το τερματίσει.
Αντίστοιχα η εφαρμογή του .. τηλεφώνου, ή του NFC, είναι κάτι που δεν θέλουμε σε καμία περίπτωση να τερματιστεί από τον LMK, οπότε το "-12" τους ταιριάζει. Φανταστείτε να θέλετε να στείλετε κάτι με NFC, και να είχε kill-άρει o LMK το αντίστοιχο process. Φρίκη!
Η εφαρμογή που τρέχουμε στο προσκήνιο αποκτά oom_adj 0, το οποίο επίσης σημαίνει ότι ο LMK δεν θα "θελήσει" να την επιλέξει για τερματισμό, όπως φαίνεται και από το σχόλιο της Google στον κώδικα:
Αντίστοιχα:
Πέρα όμως από όλα τα παραπάνω που είναι αρκετά "κρίσιμα", τι άλλο θα θέλαμε να αποφύγει να "τερματίσει" ο LMK, και στο οποίο χρησιμοποιούμε συνεχώς στην συσκευή μας? Τον Launcher!
Αλλά φυσικά η λογική δεν είναι "τόσο" απλή, καθότι είναι και κάποια άλλα πράγματα που καλά κάνει και λαμβάνει υπ' όψην ο LMK. Για παράδειγμα είναι πολύ πιθανό να θελήσεις να ξαναανοίξεις την "ακριβώς προηγούμενη" εφαρμογή, οπότε δεν θέλουμε να πάρει μεγάλη τιμή oom_adj!
Και τέλος oom_adj 9 μέχρι 15 παίρνουν τα Cached Apps, εφαρμογές δηλαδή που ανοίξαμε και δεν "θέλουν" να κάνουν κάτι ενεργά στο Background, και προφανώς τα Activities τους δεν είναι ενεργά.
Η τελευταία κατηγορία εφαρμογών είναι αρκετά ενδιαφέρουσα, και μου δίνεται η αφορμή να απαντήσω και σε μία ερώτηση που μου έχει γίνει 2-3 φορές.
Σε συσκευές με πολύ RAM (π.χ. 2GB), ίσως έχετε παρατηρήσει, ότι αν και "ανοίγετε" τα πάντα από εφαρμογές, πάντα θα έχετε πολύ ελεύθερη RAM. Αυτό συμβαίνει γιατί το Android δεν θα κρατήσει πάνω από 24 Cached Processes στην RAM, και ούτε πάνω από 30 λεπτά.
Αν θέλετε να καταφέρετε να γεμίσετε την RAM :P, επιλέξτε εφαρμογές που θα κάνουν κάτι ενεργά στο παρασκήνιο, και όχι που γίνονται CACHED.
Στο παρακάτω screenshot συνοψίζω κάποια από τα παραπάνω points.
Υπάρχουν και κάποιες "ιδιαίτερες" περιπτώσεις, όπως π.χ. αν μία εφαρμογή Crash-άρει συχνά (και άρα θέλουμε να πάρει πόδι, το οποίο το φροντίζει ο lmk), αλλά δεν θέλω να επεκταθώ ιδιαίτερα.
Πότε όμως θα αρχίσει ο LMK να "kill-άρει" εφαρμογές? Όταν η μνήμη RAM πέσει κάτω από ποιο επίπεδο? Για την ακρίβεια ο LMK ενεργοποιείται σε παραπάνω από ένα επίπεδα "χαμηλής" μνήμης.
Τι σημαίνει όμως το παραπάνω? Έστω ότι έχουμε μία συσκευή με 1GB RAM, και η ελεύθερη RAM της πέσει κάτω από 120MB (122880KB). Ο LMK θα ενεργοποιηθεί, και θα αρχίσει να "kill-άρει" εφαρμογές της κατηγορίας CACHED_APP_MAX_ADJ, με oom_adj 15, και ούτε γάτα ούτε ζημιά!
Αν η ελεύθερη μνήμη RAM πέσει κάτω και από 96MB (98304), σειρά έχουν και οι εφαρμογές της κατηγορίας CACHED_APP_MIN_ADJ κτλ.
Τα συγκεκριμένα Memory levels διαφέρουν από συσκευή σε συσκευή.
Τα επίπεδα αυτά μπορούμε να τα διαβάσουμε από το:
cd /sys/module/lowmemorykiller/parameters
cat minfree
Οι "αριθμοί" που βλέπετε είναι σε "Pages", για να τους μετατρέψουμε σε MB πολλαπλασιάζουμε επί 0,00390625. (Αυτό βγήκε από το 4/1024 για όσους αναρωτιούνται, 1 Page=4KB
Υπάρχει η δυνατότητα να "παραμετροποιήσουμε" την λειτουργία του LMK Driver κατά το Runtime (για να μην χρειάζεται να κάνουμε compile την ROM από sources, ή unpack-repack).
Αυτό γίνεται "γράφοντας" στο minfree, ως εξής:
echo "1536,2048,4096,5120,15360,23040" > /sys/module/lowmemorykiller/parameters/minfree
Ότι αλλαγές κάνουμε στο minfree, αναιρούνται στο reboot, αν θελήσουμε να γίνονται μόνιμες φτιάχνουμε αντίστοιχο startup script στο /system/init.d/
Σε άλλα linux-o-συστήματα έχω δει o OOM Killer να λαμβάνει υπ' όψη και το badness() δηλαδή το πόσο "κακό" είναι κάθε πρόγραμμα που τρέχει. Στο Android δεν είδα να γίνεται κάτι αντίστοιχο. (?)
Λίγο καταχρηστικά χρησιμοποίησα παραπάνω την λέξη "εφαρμογές". Σκοπός μου ήταν να μην σας μπλέξω με processes και services. Ζητώ συγγνώμη για αυτό, έγινε για χάρη "απλούστευσης" του άρθρου.
Θα μπορούσα να επεκταθώ αρκετά παραπάνω, όπως και να αναλύσω κάποια "τρικ" που κάνουμε οι developers εφαρμογών, για να μην γίνει η εφαρμογή μας "εύκολο θύμα" του LMK. (Βάζοντας εικονίδιο στην notification bar όσο το app μας λειτουργεί στο παρασκήνιο)
Παρ' όλα αυτά δεν θέλω να σας κουράσω άλλο, οπότε σε πρώτη φάση ας μείνουμε στο γενικό overview της λειτουργίας του LMK.