Λαμβάνω αυτό το μήνυμα σφάλματος καθώς εκτελώ τις δοκιμές JUnit:
java.lang.OutOfMemoryError: GC overhead limit exceeded
Ξέρω τι είναι το OutOfMemoryError
, αλλά τι σημαίνει το όριο επιβάρυνσης GC; Πώς μπορώ να το λύσω αυτό;
Αυτό το μήνυμα σημαίνει ότι για κάποιο λόγο ο συλλέκτης σκουπιδιών χρειάζεται υπερβολικά πολύ χρόνο (από προεπιλογή 98% του συνολικού χρόνου CPU της διεργασίας) και ανακτά πολύ λίγη μνήμη σε κάθε εκτέλεση (από προεπιλογή 2% του σωρού).
Αυτό πρακτικά σημαίνει ότι το πρόγραμμά σας σταματά να κάνει οποιαδήποτε πρόοδο και είναι απασχολημένο με την εκτέλεση μόνο της συλλογής σκουπιδιών ανά πάσα στιγμή.
Για να αποτρέψει την εφαρμογή σας από το να απορροφά τον χρόνο της CPU χωρίς να κάνει τίποτα, η JVM πετάει αυτό το Error
ώστε να έχετε την ευκαιρία να διαγνώσετε το πρόβλημα.
Οι σπάνιες περιπτώσεις στις οποίες έχω δει να συμβαίνει αυτό είναι όταν κάποιος κώδικας δημιουργούσε τόνους προσωρινών αντικειμένων και τόνους αντικειμένων με αδύναμες αναφορές σε ένα ήδη πολύ περιορισμένο περιβάλλον μνήμης.
Δείτε αυτό το άρθρο για λεπτομέρειες (συγκεκριμένα αυτό το μέρος).
Το GC πετάει αυτή την εξαίρεση όταν δαπανάται πολύς χρόνος για τη συλλογή σκουπιδιών για πολύ μικρή απόδοση, π.χ. το 98% του χρόνου της CPU δαπανάται στο GC και ανακτάται λιγότερο από το 2% του σωρού.
Αυτό το χαρακτηριστικό έχει σχεδιαστεί για να αποτρέπει τις εφαρμογές από το να εκτελούνται για μεγάλο χρονικό διάστημα ενώ σημειώνουν ελάχιστη ή καθόλου πρόοδο επειδή ο σωρός είναι πολύ μικρός.
Μπορείτε να το απενεργοποιήσετε με την επιλογή γραμμής εντολών
-XX:-UseGCOverheadLimit
Περισσότερες πληροφορίες εδώ
EDIT: φαίνεται ότι κάποιος μπορεί να γράψει πιο γρήγορα από μένα :)
Συνήθως είναι ο κώδικας. Εδώ είναι ένα απλό παράδειγμα:
import java.util.*;
public class GarbageCollector {
public static void main(String... args) {
System.out.printf("Testing...%n");
List<Double> list = new ArrayList<Double>();
for (int outer = 0; outer < 10000; outer++) {
// list = new ArrayList<Double>(10000); // BAD
// list = new ArrayList<Double>(); // WORSE
list.clear(); // BETTER
for (int inner = 0; inner < 10000; inner++) {
list.add(Math.random());
}
if (outer % 1000 == 0) {
System.out.printf("Outer loop at %d%n", outer);
}
}
System.out.printf("Done.%n");
}
}
Χρησιμοποιώντας java 1.6.0_24-b07 σε ένα Windows7 32 bit.
java -Xloggc:gc.log GarbageCollector
Στη συνέχεια, κοιτάξτε το gc.log
Τώρα, αυτό δεν είναι το καλύτερο τεστ ή ο καλύτερος σχεδιασμός, αλλά όταν αντιμετωπίζετε μια κατάσταση όπου δεν έχετε άλλη επιλογή από το να υλοποιήσετε έναν τέτοιο βρόχο ή όταν έχετε να κάνετε με υπάρχοντα κώδικα που συμπεριφέρεται άσχημα, η επιλογή της επαναχρησιμοποίησης αντικειμένων αντί της δημιουργίας νέων μπορεί να μειώσει τον αριθμό των φορών που ο συλλέκτης σκουπιδιών μπαίνει στη μέση...