Γράφω μια εφαρμογή Swing στην πλευρά του πελάτη (γραφικός σχεδιαστής γραμματοσειρών) σε Java 5. Πρόσφατα, αντιμετωπίζω το σφάλμα java.lang.OutOfMemoryError: Java heap space
σφάλμα επειδή δεν είμαι συντηρητικός στη χρήση της μνήμης. Ο χρήστης μπορεί να ανοίξει απεριόριστο αριθμό αρχείων και το πρόγραμμα διατηρεί τα αντικείμενα που ανοίχτηκαν στη μνήμη. Μετά από μια γρήγορη έρευνα βρήκα το Ergonomics in the 5.0 Java Virtual Machine και άλλα που λένε ότι σε μηχανή Windows η JVM προεπιλέγει μέγιστο μέγεθος σωρού ως 64MB
.
Δεδομένης αυτής της κατάστασης, πώς πρέπει να αντιμετωπίσω αυτόν τον περιορισμό;
Θα μπορούσα να αυξήσω το max heap size χρησιμοποιώντας την επιλογή command line της java, αλλά αυτό θα απαιτούσε να υπολογίσω τη διαθέσιμη μνήμη RAM και να γράψω κάποιο πρόγραμμα ή σενάριο εκκίνησης. Εκτός αυτού, η αύξηση σε κάποιο πεπεπερασμένο μέγιστο δεν εξαλείφει τελικά το πρόβλημα.
Θα μπορούσα να ξαναγράψω κάποιον από τον κώδικά μου ώστε να διατηρώ συχνά αντικείμενα στο σύστημα αρχείων (η χρήση βάσης δεδομένων είναι το ίδιο πράγμα) για να απελευθερώσω τη μνήμη. Θα μπορούσε να δουλέψει, αλλά πιθανόν να είναι και πολύ δουλειά.
Αν μπορούσατε να μου υποδείξετε λεπτομέρειες για τις παραπάνω ιδέες ή κάποιες εναλλακτικές λύσεις όπως αυτόματη εικονική μνήμη, δυναμική επέκταση του μεγέθους του σωρού, αυτό θα ήταν υπέροχο.
Τελικά, έχετε πάντα ένα πεπερασμένο μέγιστο σωρό προς χρήση, ανεξάρτητα από την πλατφόρμα στην οποία εκτελείτε. Στα Windows 32 bit αυτό είναι περίπου 2GB
(όχι συγκεκριμένα σωρός αλλά συνολικό ποσό μνήμης ανά διεργασία). Απλά τυχαίνει η Java να επιλέγει να κάνει την προεπιλογή μικρότερη (προφανώς για να μην μπορεί ο προγραμματιστής να δημιουργήσει προγράμματα που έχουν ανεξέλεγκτη κατανομή μνήμης χωρίς να πέσει πάνω σε αυτό το πρόβλημα και να πρέπει να εξετάσει τι ακριβώς κάνει).
Έτσι αυτό δεδομένο υπάρχουν διάφορες προσεγγίσεις που θα μπορούσατε να ακολουθήσετε είτε για να προσδιορίσετε το μέγεθος της μνήμης που χρειάζεστε είτε για να μειώσετε το μέγεθος της μνήμης που χρησιμοποιείτε. Ένα συνηθισμένο λάθος με τις γλώσσες που συλλέγουν τα σκουπίδια, όπως η Java ή η C#, είναι να διατηρείτε αναφορές σε αντικείμενα που δεν χρησιμοποιείτε πλέον, ή να κατανέμετε πολλά αντικείμενα ενώ θα μπορούσατε να τα επαναχρησιμοποιήσετε αντ' αυτού. Όσο τα αντικείμενα έχουν μια αναφορά σε αυτά, θα συνεχίσουν να χρησιμοποιούν χώρο στο σωρό, καθώς ο συλλέκτης σκουπιδιών δεν θα τα διαγράψει.
Σε αυτή την περίπτωση μπορείτε να χρησιμοποιήσετε έναν αναλυτή μνήμης της Java για να προσδιορίσετε ποιες μέθοδοι στο πρόγραμμά σας δεσμεύουν μεγάλο αριθμό αντικειμένων και στη συνέχεια να προσδιορίσετε αν υπάρχει τρόπος να βεβαιωθείτε ότι δεν υπάρχει πλέον αναφορά σε αυτά ή να μην τα δεσμεύσετε εξαρχής. Μια επιλογή που έχω χρησιμοποιήσει στο παρελθόν είναι το "JMP" http://www.khelekore.org/jmp/.
Αν διαπιστώσετε ότι κατανέμετε αυτά τα αντικείμενα για κάποιο λόγο και πρέπει να διατηρήσετε γύρω σας αναφορές (ανάλογα με το τι κάνετε μπορεί να είναι αυτή η περίπτωση), θα πρέπει απλώς να αυξήσετε το μέγιστο μέγεθος του σωρού όταν ξεκινάτε το πρόγραμμα. Ωστόσο, μόλις κάνετε το προφίλ μνήμης και καταλάβετε πώς κατανέμονται τα αντικείμενά σας, θα έχετε μια καλύτερη ιδέα για το πόση μνήμη χρειάζεστε.
Γενικά, αν δεν μπορείτε να εγγυηθείτε ότι το πρόγραμμά σας θα εκτελεστεί σε κάποιο πεπερασμένο ποσό μνήμης (ίσως ανάλογα με το μέγεθος της εισόδου), θα αντιμετωπίζετε πάντα αυτό το πρόβλημα. Μόνο αφού εξαντλήσετε όλα αυτά θα χρειαστεί να εξετάσετε την προσωρινή αποθήκευση αντικειμένων στο δίσκο κλπ. Σε αυτό το σημείο θα πρέπει να έχετε έναν πολύ καλό λόγο για να πείτε "Χρειάζομαι Xgb μνήμης" για κάτι και δεν μπορείτε να το παρακάμψετε βελτιώνοντας τους αλγορίθμους σας ή τα μοτίβα κατανομής μνήμης. Γενικά, αυτό θα ισχύει συνήθως μόνο για αλγορίθμους που λειτουργούν σε μεγάλα σύνολα δεδομένων (όπως μια βάση δεδομένων ή κάποιο πρόγραμμα επιστημονικής ανάλυσης) και τότε τεχνικές όπως η προσωρινή αποθήκευση και το memory mapped IO γίνονται χρήσιμες.
Εκτελέστε τη Java με την επιλογή της γραμμής εντολών -Xmx
, η οποία ορίζει το μέγιστο μέγεθος του σωρού.
Ναι, με το -Xmx
μπορείτε να ρυθμίσετε περισσότερη μνήμη για το JVM σας.
Για να είστε σίγουροι ότι δεν θα έχετε διαρροή ή σπατάλη μνήμης. Κάντε μια απόρριψη σωρού και χρησιμοποιήστε το Eclipse Memory Analyzer για να αναλύσετε την κατανάλωση μνήμης.