Δομές Δεδομένων Ενότητα 3: Διαχείριση μνήμης Δρ. Γεώργιος Σίσιας Τμήμα Μηχανικών Πληροφορικής ΤΕ
Άδειες Χρήσης Το παρόν εκπαιδευτικό υλικό υπόκειται σε άδειες χρήσης Creative Commons. Για εκπαιδευτικό υλικό, όπως εικόνες, που υπόκειται σε άλλου τύπου άδειας χρήσης, η άδεια χρήσης αναφέρεται ρητώς. 2
Χρηματοδότηση Το παρόν εκπαιδευτικό υλικό έχει αναπτυχθεί στα πλαίσια του εκπαιδευτικού έργου του διδάσκοντα. Το έργο «Ανοικτά Ακαδημαϊκά Μαθήματα στο TEI Δυτικής Μακεδονίας και στην Ανώτατη Εκκλησιαστική Ακαδημία Θεσσαλονίκης» έχει χρηματοδοτήσει μόνο τη αναδιαμόρφωση του εκπαιδευτικού υλικού. Το έργο υλοποιείται στο πλαίσιο του Επιχειρησιακού Προγράμματος «Εκπαίδευση και Δια Βίου Μάθηση» και συγχρηματοδοτείται από την Ευρωπαϊκή Ένωση (Ευρωπαϊκό Κοινωνικό Ταμείο) και από εθνικούς πόρους. 3
Σκοποί ενότητας Η ενότητα αυτή πραγματεύεται το ζήτημα της διαχείρισης μνήμης. 4
Περιεχόμενα ενότητας Στατική μνήμη. Δυναμική μνήμη. Ασκήσεις. Βιβλιογραφία. 5
Στατική μνήμη (1/6) Η μνήμη που χρησιμοποιεί ένα πρόγραμμα χωρίζεται σε δύο κατηγορίες, ανάλογα με τον τρόπο διαχείρισης και χρήσης της. Η μία από αυτές τις κατηγορίες ονομάζεται στοίβα (stack) και είναι στατικά δεσμευμένη από τη στιγμή που ξεκινάει το πρόγραμμα. Το μέγεθός της καθορίζεται κατά τη διάρκεια μεταγλώττισης, μέσω μιας επιλογής του λογισμικού ανάπτυξης εφαρμογών. 6
Στατική μνήμη (2/6) Η τυπική της τιμή είναι 4 ΜΒ ή 10 ΜΒ (αυτό ενδέχεται να διαφέρει από εργαλεία σε εργαλείο και έκδοση σε έκδοση, ή ίσως και άλλους παράγοντες). Στη μνήμη αυτή που το πρόγραμμα διαχειρίζεται ως στοίβα μπαίνουν οι ολικές και οι τοπικές μεταβλητές, καθώς και οι παράμετροι των συναρτήσεων όταν αυτές καλούνται. 7
Στατική μνήμη (3/6) Σε περίπτωση εξάντλησης αυτής της ποσότητας μνήμης, το πρόγραμμα καταρρέει με μήνυμα της μορφής «stack overflow» ή «υπερχείλιση στοίβας». Αν διαπιστώσετε ότι αυτή η ποσότητα μνήμης δεν επαρκεί στο πρόγραμμά σας, τότε μπορείτε να αλλάξετε την αντίστοιχη ρύθμιση και να μεταγλωττίσετε ξανά το πρόγραμμά σας. 8
Στατική μνήμη (4/6) Ωστόσο πρέπει να έχετε υπόψη τα εξής δύο, όσον αφορά μια τέτοια τακτική: 1. Αν αυξήσετε το όριο αυτό, τότε θα πρέπει να γνωρίζετε ότι έτσι θα αυξήσετε και τις αρχικές απαιτήσεις του προγράμματός σας. 2. Σε περίπτωση που χρειάζεστε μεγάλες ποσότητες μνήμης, τότε καλό είναι να τις δεσμεύετε δυναμικά, όπως θα δούμε σε λίγο. 9
Στατική μνήμη (5/6) Δοκιμάστε το ακόλουθο πρόγραμμα: #include <stdio.h> #define N 10000000 void main() { double int i; BigArray[N]; } for ( i = 0; i < N; i++ ) BigArray[i] = 0.0; 10
Στατική μνήμη (6/6) Θα διαπιστώσετε ότι το παραπάνω πρόγραμμα "σταμάτησε να λειτουργεί". Ο λόγος είναι αυτός που αναφέραμε πιο πάνω ο πίνακας BigArray απαιτεί χώρο κάτι λιγότερο από 80 ΜΒ. 11
Δυναμική μνήμη (1/16) Η προσέγγιση για να ξεπεραστεί ο παραπάνω περιορισμός είναι η δυναμική δέσμευση μνήμης (σωρός/heap). Δηλαδή, το πρόγραμμα ζητάει να δεσμεύσει πόρους (όπως μνήμη) κατά τη διάρκεια εκτέλεσης του προγράμματος, και ανάλογα με τις εκάστοτε απαιτήσεις. 12
Δυναμική μνήμη (2/16) Εδώ θα πρέπει να θυμηθείτε τα όρια των 32- και 64-bit ΛΣ που αναφέραμε σε προηγούμενη ενότητα. Ακόμη, θα πρέπει να λάβετε υπόψη το μέγεθος της μνήμης και το μέγεθος του αρχείου σελιδοποίησης/μνήμης του υπολογιστή σας. Ο μηχανισμός δυναμικής δέσμευσης μνήμης λειτουργεί με τη βοήθεια δεικτών και κατάλληλων εντολών για τη δέσμευση και αποδέσμευση (δυναμικά) της απαιτούμενης μνήμης. 13
Δυναμική μνήμη (3/16) Στη γλώσσα C έχουμε τις εντολές malloc, free, και realloc, ενώ στη γλώσσα C++ έχουμε για τον ίδιο σκοπό και δύο τελεστές, τους new και delete, αντίστοιχα. Παρακάτω έχουμε ένα πρόγραμμα που δεσμεύει μνήμη δυναμικά, το οποίο ξεπερνά κατά πολύ τη ζητούμενη μνήμη του παραπάνω παραδείγματος στατικής δέσμευσης μνήμης, χωρίς να καταρρέει το πρόγραμμα. 14
Δυναμική μνήμη (4/16) #include <stdio.h> #include <stdlib.h> #define N 134217728 void main() { double int i; *BigArray; BigArray = (double *) malloc(sizeof(double) * N); 15
Δυναμική μνήμη (5/16) // Check if there was enough memory for the block. if ( BigArray == NULL ) { printf("failed to allocate memory."); exit(1); } // if } for ( i = 0; i < N; i++ ) BigArray[i] = 0.0; free(bigarray); 16
Δυναμική μνήμη (6/16) Το παραπάνω πρόγραμμα προσπαθεί να δεσμεύσει δυναμικά μνήμη 1 GB. Αν δεν τα καταφέρει, τότε θα εμφανίσει μήνυμα λάθους ("Failed to allocate memory.") και θα τερματίσει. Εσείς ίσως διαπιστώσετε ότι το 1 GB είναι υπερβολικό για τον υπολογιστή που δουλεύετε. 17
Δυναμική μνήμη (7/16) Μπορείτε να μειώσετε το Ν ώστε να πετύχει η δέσμευση μνήμης, προσέχοντας, όμως, να είναι τουλάχιστον όση και η στατική που προσπαθήσατε να δεσμεύσετε ανεπιτυχώς (π.χ. όταν το Ν είναι 13107200, η ζητούμενη μνήμη είναι 100 ΜΒ). Ας εξηγήσουμε το παραπάνω πρόγραμμα. 18
Δυναμική μνήμη (8/16) Στην αρχή του προγράμματος χρειαζόμαστε τη βιβλιοθήκη stdlib.h ώστε να έχουμε πρόσβαση στις συναρτήσεις malloc, free, και realloc. Στο κυρίως πρόγραμμα δηλώνουμε δείκτη τύπου double με όνομα BigArray. Ακολουθεί δήλωση ακεραίου i. Η malloc χρειάζεται μία παράμετρο που να περιέχει την ποσότητα μνήμης που θα προσπαθήσει να δεσμεύσει (σε bytes). 19
Δυναμική μνήμη (9/16) Επιστρέφει δείκτη τύπου "void *" που δείχνει στην αρχή του μπλοκ μνήμης που δέσμευσε επιτυχώς. Γι αυτό το λόγο κάνουμε αλλαγή τύπου (type casting) με την παρένθεση (double *). Παρατηρήστε ότι στην παρένθεση αυτή βάζουμε τύπο double, επειδή και η δήλωση του δείκτη είναι, εν προκειμένω, double. 20
Δυναμική μνήμη (10/16) Μετέπειτα, με την εντολή malloc, προσπαθούμε να δεσμεύσουμε μνήμη ίση με περίπου 134 εκατομμύρια αριθμούς double, οι οποίοι απαιτούν 8 bytes χώρο έκαστος. Έτσι, η παράμετρος της malloc ουσιαστικά ζητάει 1 GB RAM. Είναι φυσικό να υπάρχει το ενδεχόμενο να μην είναι διαθέσιμη η απαιτούμενη μνήμη, οπότε, σε μια τέτοια περίπτωση, ο δείκτης που επιστρέφει η malloc έχει τιμή NULL. 21
Δυναμική μνήμη (11/16) Το NULL στην ουσία είναι 0, αλλά εμείς δε συγκρίνουμε ΠΟΤΕ δείκτη με 0, ώστε να είμαστε σίγουροι ότι το πρόγραμμά μας θα συνεχίσει να δουλεύει και σε περίπτωση που η τιμή του NULL αλλάξει. Ωστόσο, ΠΑΝΤΟΤΕ ελέγχουμε αν ένας δείκτης είναι έγκυρος (δηλαδή διάφορος του NULL). 22
Δυναμική μνήμη (12/16) Έτσι, σε περίπτωση που δεν είχαμε αρκετή μνήμη ή για κάποιον άλλο λόγω απέτυχε η δέσμευση, το πρόγραμμα αναφέρει μήνυμα λάθους (όπως με την if που βλέπετε παραπάνω) και τερματίζει πριν τη χρησιμοποιήσει. Στο βρόχο for που ακολουθεί αρχικοποιούμε όλη αυτή τη μνήμη (την οποία σημειωτέον μπορούμε να χρησιμοποιήσουμε και ως μονοδιάστατο πίνακα) σε 0. 23
Δυναμική μνήμη (13/16) Τέλος, αφού τελειώσουμε με τη μνήμη που δεσμεύσαμε, πρέπει ΠΑΝΤΟΤΕ να την αποδεσμεύουμε. Πάρ' όλα αυτά, όταν ένα πρόγραμμα τερματίζει, το ΛΣ παίρνει αυτόματα πίσω όλη τη μνήμη που του δέσμευσε κατά τη διάρκεια εκτέλεσης του προγράμματος. Ωστόσο, η σωστή πρακτική είναι η αποδέσμευση της μνήμης όταν αυτή δε χρειάζεται πια, και σίγουρα πριν τον τερματισμό του. 24
Δυναμική μνήμη (14/16) Ακολουθεί η εντολή free, η οποία δέχεται ως μοναδική παράμετρο το δείκτη στη διεύθυνση μνήμης που δέσμευσε μια malloc. Πρέπει να βεβαιωθείτε ότι ο δείκτης που περνάτε στη free είναι έγκυρος, αλλιώς το πρόγραμμα καταρρέει. 25
Δυναμική μνήμη (15/16) Εδώ θα αναφέρουμε συνοπτικά και την εντολή realloc, η οποία δέχεται τον παλιό δείκτη και την καινούργια ποσότητα μνήμης που θέλουμε. Σκοπός της είναι να αλλάξει τη δεσμευμένη ποσότητα μνήμης (μικρότερη ή μεγαλύτερη από πριν), χωρίς να χρειαστεί η δέσμευση ενός δεύτερου μπλοκ μνήμης, η αντιγραφή των στοιχείων του παλαιού μπλοκ στο καινούργιο, και η αποδέσμευση του παλαιού. Αυτό επιτυγχάνεται με τη βοήθεια κατάλληλων εντολών του ΛΣ. 26
Δυναμική μνήμη (16/16) Για περισσότερα, αναφερθείτε στο σύνδεσμο http://www.cplusplus.com/reference/cstdlib/realloc/. Οι αντίστοιχες εντολές στη C++ είναι οι τελεστές new και delete. Εδώ δε θα πούμε περισσότερα γι' αυτές. Ωστόσο, πρέπει να αναφέρουμε ότι αντενδείκνυται η χιαστή χρήση τους (malloc με delete ή/και new με free). 27
Ασκήσεις Άσκηση 1. Υλοποιήστε την παραπάνω 2η εργαστηριακή άσκηση σχετικά με τους δείκτες, αλλά αυτή τη φορά να διαχειριστείτε τη μνήμη δυναμικά. Άσκηση 2. Προσπαθήστε να βρείτε η μέγιστη ποσότητα μνήμης που μπορείτε να δεσμεύσετε δυναμικά κάτω από 32-bit στον υπολογιστή σας. Βοήθεια: Κάντε απόπειρες δέσμευσης ολοένα και μεγαλύτερων ή μικρότερων ποσοτήτων μέχρι αυτές να πετύχουν ή να αποτύχουν. Μπορείτε να χρησιμοποιήστε το παράδειγμα της παρούσας ενότητας ώστε να ξεκινήσετε. 28
Βιβλιογραφία (1/8) Aho AV, Hopcroft JE, and Ullman JD. (1974) The design and analysis of computer algorithms. USA, Addison-Wesley Publishing Company. 470 pp. ISBN 0-201-00029-6. ( 38.33, 46.76). Aho AV, Hopcroft JE, and Ullman JD. (1983) Data structures and algorithms. USA, Addison-Wesley Publishing Company. 427 pp. ISBN 0-201-00023-7. ( 43.65, 53.25). Bik AJC. (2004) The software vectorization handbook: applying multimedia extensions for maximum performance. USA, Intel Press. 236 pp. ISBN 0-9743649-2-4. ( 32.31, 46.63). 29
Βιβλιογραφία (2/8) Carrano FM and Henry T. (2013) Data abstraction and problem solving with C++: walls and mirrors. 6th ed. UK, Pearson Education Limited. 833 pp. ISBN10 0-273-76841-7, ISBN13 978-0-76841-8. Cormen TH, Leiserson CE, Rivest RL, and Stein C. (2012) Εισαγωγή στους αλγορίθμους. USA/Ελλάδα, MIT Press/Ίδρυμα Τεχνολογίας & Έρευνας - Πανεπιστημιακές Εκδόσεις Κρήτης. 1260 pp. ISBN 978-960-524-224-4. Dasgupta S, Papadimitriou C and Vazirani U. (2009) Αλγόριθμοι. Ελλάδα, Εκδόσεις Κλειδάριθμος. 416 σελ. ISBN13 978-960-461-211-6. 30
Βιβλιογραφία (3/8) Gerber R, Bik AJC, Smith KB and Tian X. (2006) The software optimization cookbook: high-performance recipes for IA-32 platforms. 2nd ed. USA, Intel Press. 404 pp. ISBN 0-9764832-1-1. ( 35.07, 50.61). Helman P and Veroff R. (1988) Walls and mirrors: intermediate problem solving and data structures. Modula 2 ed. USA, The Benjamin/Cummings Publishing Company, Inc. 625 pp. ( 23.95). Kleinberg J and Tardos W. (2008) Σχεδιασμός αλγορίθμων. Ελλάδα, Εκδόσεις Κλειδάριθμος. 944 σελ. ISBN13 978-960- 461-207-9. 31
Βιβλιογραφία (4/8) Knuth DE. (2009) Η τέχνη του προγραμματισμού: θεμελιώδεις αλγόριθμοι, Τόμος Α. 3η Έκδοση. Ελλάδα, Εκδόσεις Τζιόλα. 759 σελ. ISBN13 978-960-418-185-8. Knuth DE. (2010) Η τέχνη του προγραμματισμού: ημιαριθμητικοί αλγόριθμοι, Τόμος Β. 3η Έκδοση. Ελλάδα, Εκδόσεις Τζιόλα. 912 σελ. ISBN13 978-960-418-224-4. Knuth DE. (2010) Η τέχνη του προγραμματισμού: ταξινόμηση και αναζήτηση, Τόμος Γ. 2η Έκδοση. Ελλάδα, Εκδόσεις Τζιόλα. 926 σελ. ISBN13 978-960-418-245-9. 32
Βιβλιογραφία (5/8) Kruse RL and Ryba AJ. (1999) Data structures and program design in C++. USA, Prentice Hall. 717 pp. ISBN 0-13- 082640-5. ( 40.74). Lafore R. (2005) Δομές δεδομένων και αλγόριθμοι στη Java. Ελλάδα, Εκδόσεις Μ. Γκιούρδα. 798 σελ. ISBN10 960-512- 452-1. Levitin A. (2008) Εισαγωγή στην ανάλυση και σχεδίαση αλγορίθμων. 2η Έκδοση. Ελλάδα, Εκδόσεις Τζιόλα. 700 σελ. ISBN13 978-960-418-143-8. 33
Βιβλιογραφία (6/8) McConnell S. (1993) Code complete: a practical handbook of software construction. USA, Microsoft Press. 857 pp. ( 25.99). Mehlhorn K and Sanders P. (2008) Algorithms and data structures: the basic toolbox. Germany, Springer-Verlag Berlin Heidelberg. 300 pp. ISBN13 978-3-540-77977-3. ( 28.11, 34.29). Prichard JJ and Carrano FM. (2011) Data abstraction and problem solving with Java: walls and mirrors. 3rd ed. UK, Pearson Education Limited. 959 pp. ISBN10 0-273-75120-4. ISBN13 978-0-273-75120-5. 34
Βιβλιογραφία (7/8) Sahni S. (2004) Δομές Δεδομένων, Αλγόριθμοι, και Εφαρμογές στη C++. Ελλάδα/ΗΠΑ, Εκδόσεις Τζιόλα/McGraw-Hill. 830 σελ. ISBN10 960-418-030-4. Sedgewick R and Flajolet P. (2013) An introduction to the analysis of algorithms. 2nd ed. USA, Pearson Education, Inc. 572 pp. ISBN13 978-0-321-90575-8. Sedgewick R and Wayne K. (2011) Algorithms. 4th ed. USA, Pearson Education, Inc. 952 pp. ISBN13 978-0-321-57351-3. Κοίλιας Χ. (2004) Δομές δεδομένων & οργανώσεις αρχείων. Ελλάδα, Εκδόσεις Νέων Τεχνολογιών. 447 σελ. ISBN10 960-8105-64-1. 35
Βιβλιογραφία (8/8) Μποζάνης ΠΔ. (2006) Δομές Δεδομένων. Ελλάδα, Εκδόσεις Τζιόλα. 552 σελ. ISBN10 960-418-084-3. Μποζάνης ΠΔ. (2009) Προβλήματα & ασκήσεις στους αλγορίθμους. Ελλάδα, Εκδόσεις Τζιόλα. 492 σελ. ISBN13 978-960-418-186-5. Μποζάνης ΠΔ. (2013) Αλγόριθμοι. Ελλάδα, Εκδόσεις Τζιόλα. 549 σελ. ISBN13 978-960-418-070-1. Μυσιρλής Ν. (2002) Δομές δεδομένων με C. Ελλάδα, αυτοέκδοση. 347 σελ. ISBN10 960-92031-1-6. Παπαρίζος Κ. (2010) Ανάλυση και σχεδίαση αλγορίθμων. Ελλάδα, Εκδόσεις Τζιόλα. 606 σελ. ISBN13 978-960-418-222-0. 36
Τέλος Ενότητας
Σημείωμα Αναφοράς Copyright ΤΕΙ Δυτικής Μακεδονίας, Δρ. Γεώργιος Σίσιας. «Δομές Δεδομένων». Έκδοση: 1.0. Κοζάνη 2015. Διαθέσιμο από τη δικτυακή διεύθυνση: URL. 38
Σημείωμα Αδειοδότησης Το παρόν υλικό διατίθεται με τους όρους της άδειας χρήσης Creative Commons Αναφορά, Μη Εμπορική Χρήση Παρόμοια Διανομή 4.0 [1] ή μεταγενέστερη, Διεθνής Έκδοση. Εξαιρούνται τα αυτοτελή έργα τρίτων π.χ. φωτογραφίες, διαγράμματα κ.λ.π., τα οποία εμπεριέχονται σε αυτό και τα οποία αναφέρονται μαζί με τους όρους χρήσης τους στο «Σημείωμα Χρήσης Έργων Τρίτων». [1] http://creativecommons.org/licenses/by-nc-sa/4.0/ Ως Μη Εμπορική ορίζεται η χρήση: που δεν περιλαμβάνει άμεσο ή έμμεσο οικονομικό όφελος από την χρήση του έργου, για το διανομέα του έργου και αδειοδόχο. που δεν περιλαμβάνει οικονομική συναλλαγή ως προϋπόθεση για τη χρήση ή πρόσβαση στο έργο. που δεν προσπορίζει στο διανομέα του έργου και αδειοδόχο έμμεσο οικονομικό όφελος (π.χ. διαφημίσεις) από την προβολή του έργου σε διαδικτυακό τόπο. Ο δικαιούχος μπορεί να παρέχει στον αδειοδόχο ξεχωριστή άδεια να χρησιμοποιεί το έργο για εμπορική χρήση, εφόσον αυτό του ζητηθεί. 39
Διατήρηση Σημειωμάτων Οποιαδήποτε αναπαραγωγή ή διασκευή του υλικού θα πρέπει να συμπεριλαμβάνει: το Σημείωμα Αναφοράς. το Σημείωμα Αδειοδότησης. τη δήλωση Διατήρησης Σημειωμάτων. το Σημείωμα Χρήσης Έργων Τρίτων (εφόσον υπάρχει). μαζί με τους συνοδευόμενους υπερσυνδέσμους. 40