Διαχείριση μνήμης σε Java

⚡ Έξυπνη Σύνοψη

Διαχείριση μνήμης σε Java εξηγεί πώς η JVM διαιρεί τη μνήμη χρόνου εκτέλεσης σε Stack, Heap, Codeκαι στατικές περιοχές, πώς ρέουν οι αναφορές αντικειμένων κατά τη διάρκεια των κλήσεων μεθόδων και πώς το Garbage Collector ανακτά τα μη προσβάσιμα αντικείμενα, ώστε οι εφαρμογές να παραμένουν σταθερές και απαλλαγμένες από διαρροές μνήμης.

  • 🧠 Διάταξη μνήμης JVM: Java Η μνήμη χωρίζεται σε Heap για αντικείμενα, Stack για πλαίσια μεθόδων και τοπικές μεταβλητές, καθώς και σε ειδικές περιοχές για bytecode και στατικά δεδομένα.
  • 📚 Στοίβα vs Σωρός: Τα πλαίσια στοίβας ακολουθούν τη σειρά Τελευταίο-Εισαγόμενο-Πρώτο-Εξαγόμενο και αποθηκεύουν πρωτόγονα στοιχεία και αναφορές, ενώ το Heap περιέχει κάθε αντικείμενο που δημιουργείται με τον νέο τελεστή.
  • ♻️ Συλλογή απορριμάτων: Ο Σκουπιδοσυλλέκτης tracΗ προσβασιμότητα του ks μέσω αλυσίδων αναφοράς και ανακτά αυτόματα τη μνήμη από αντικείμενα που κανένα νήμα δεν μπορεί να φτάσει.
  • Σύγχρονοι Συλλέκτες: Τα φόρτα εργασίας παραγωγής βασίζονται όλο και περισσότερο στα G1, ZGC και Shenandoah για συλλογή με χαμηλή παύση σε σωρούς πολλών gigabyte.
  • 🧪 Πρακτικό παράδειγμα: Μια αναλυτική παρουσίαση της τάξης Student δείχνει πότε οι αναφορές γίνονται επιλέξιμες για συλλογή και πώς η μηδενική αναφορά απελευθερώνει το υποκείμενο αντικείμενο.

Διαχείριση μνήμης σε Java

Τι είναι η μνήμη στοίβας; Java?

Στοίβα μνήμης σε Java είναι η περιοχή της μνήμης JVM που αποθηκεύει πλαίσια μεθόδων, τοπικές μεταβλητές και μεταβλητές αναφοράς για κάθε νήμα. Η στοίβα προσπελαύνεται πάντα με τη σειρά Τελευταία-Εισαγωγή-Πρώτη-Έξοδος, επομένως η μέθοδος που κλήθηκε πιο πρόσφατα βρίσκεται στην κορυφή και οι τοπικές μεταβλητές της ωθούνται και εμφανίζονται μαζί με το πλαίσιο.

Κάθε νήμα στο Java Η Εικονική Μηχανή λαμβάνει τη δική της Στοίβα, η οποία διατηρεί τις κλήσεις μεθόδων απομονωμένες και ασφαλείς για τα νήματα εκ κατασκευής. Πρωτόγονες τοπικές τιμές όπως int, boolean και double βρίσκονται απευθείας μέσα στο πλαίσιο, ενώ οι αναφορές αντικειμένων που είναι αποθηκευμένες στη Στοίβα δείχνουν σε αντικείμενα που έχουν εκχωρηθεί στο Heap.

Τι είναι η μνήμη σωρού στο Java?

Η μνήμη σωρού είναι η κοινόχρηστη περιοχή JVM που περιέχει κάθε Java αντικείμενο και πίνακας που δημιουργούνται με τον νέο τελεστή, μαζί με τυχόν μεταβλητές αναφοράς που ανήκουν σε αυτά τα αντικείμενα ως πεδία στιγμιότυπων. Σε αντίθεση με τη Στοίβα, το Heap είναι κοινόχρηστο σε όλα τα νήματα, γι' αυτό και η πρόσβαση σε αντικείμενα συχνά απαιτεί συγχρονισμό.

Το Heap είναι η περιοχή που διαχειρίζεται το Garbage Collector. Τα σύγχρονα HotSpot JVMs διαιρούν το Heap σε μια Νεαρή Γενιά για βραχύβια αντικείμενα και μια Παλιά Γενιά για αντικείμενα μακράς διαρκείας, με τα μεταδεδομένα κλάσης να αποθηκεύονται σε μια ξεχωριστή εγγενή περιοχή που ονομάζεται Metaspace.

Εκχώρηση μνήμης σε Java

Εκχώρηση μνήμης σε Java είναι η διαδικασία με την οποία η JVM ορίζει περιοχές εικονικής μνήμης για μεταβλητές και στιγμιότυπα κλάσεων και δομών κατά την εκτέλεση του προγράμματος. Η μνήμη δεν εκχωρείται σε ένα αντικείμενο κατά τη δήλωση. Δημιουργείται μόνο μια αναφορά. Η πραγματική εκχώρηση αντικειμένου γίνεται μέσω του τελεστή new, επομένως κάθε αντικείμενο βρίσκεται στο Heap.

The Java Η κατανομή μνήμης χωρίζεται στις ακόλουθες ενότητες:

  1. Σωρός
  2. Στοίβα
  3. Code
  4. Στατικός

Αυτή η διαίρεση μνήμης είναι απαραίτητη για την αποτελεσματική διαχείριση του χρόνου εκτέλεσης.

  • The Code η ενότητα περιέχει το μεταγλωττισμένο σας bytecode.
  • The Στοίβα καταστήματα τμήματος μέθοδοι, τοπικές μεταβλητές και μεταβλητές αναφοράς.
  • The Σωρός ενότητα περιέχει αντικειμένων και μπορεί επίσης να περιέχει μεταβλητές αναφοράς που διατηρούνται ως πεδία στιγμιότυπου.
  • The Στατικός τμήμα κατέχει στατικά δεδομένα και στατικές μέθοδοι κοινόχρηστο σε όλες τις περιπτώσεις.

Διαφορά μεταξύ τοπικής και μεταβλητής παρουσίας

Η κατανόηση του πού βρίσκεται κάθε είδος μεταβλητής στην JVM βοηθά στην εξήγηση της συμπεριφοράς της στοίβας και της στοίβας. μεταβλητή παράδειγμα δηλώνεται μέσα σε μια κλάση αλλά έξω από οποιαδήποτε μέθοδο, και βρίσκεται στο Σωρό ως μέρος του αντικειμένου του.

class Student{ 
int num; // num is instance variable 
public void showData{}

A τοπική μεταβλητή δηλώνεται μέσα σε μια μέθοδο, συμπεριλαμβανομένων των ορισμάτων της μεθόδου, και βρίσκεται στη Στοίβα μέσα στο ενεργό πλαίσιο.

public void sum(int a){

int x = a + 3;

// a, x are local variables

}

Διαφορά μεταξύ Stack και Heap

Η Στοίβα και ο Σωρός λύνουν διαφορετικά προβλήματα μέσα στην JVM. Η Στοίβα δίνει σε κάθε νήμα γρήγορη, ντετερμινιστική κατανομή για βραχύβια δεδομένα που συνδέονται με το πεδίο εφαρμογής της μεθόδου, ενώ ο Σωρός παρέχει μια κοινόχρηστη περιοχή για αντικείμενα μακράς διάρκειας στα οποία μπορεί να αναφερθεί οποιοδήποτε νήμα. Το σύντομο βίντεο παρακάτω συνοψίζει την αντίθεση πριν από την επεξήγηση που ακολουθεί.

Πατήστε εδώ εάν το βίντεο δεν είναι προσβάσιμο

Για να δούμε πώς συνεργάζονται οι Stack και Heap, θεωρούμε μια μέθοδο main που καλεί τη μέθοδο m1.

public void m1{
int x = 20;
}

Στη στοίβα JVM, δημιουργείται ένα πλαίσιο για τη μέθοδο m1.

Java Στοίβα και Σωρός

Η μεταβλητή x στο m1 δημιουργείται επίσης στο πλαίσιο για το m1 στη Στοίβα, όπως φαίνεται στην παρακάτω εικόνα.

Java Στοίβα και Σωρός

Στη συνέχεια, η μέθοδος m1 καλεί τη μέθοδο m2. Στη στοίβα, δημιουργείται ένα νέο πλαίσιο για το m2 πάνω από το πλαίσιο για το m1.

Java Στοίβα και Σωρός

Java Στοίβα και Σωρός

Οι τοπικές μεταβλητές b και c δημιουργούνται επίσης μέσα στο πλαίσιο για το m2 στη στοίβα.

public void m2(int b){
boolean c;
}

Στη συνέχεια, το m2 καλεί τη μέθοδο m3. Και πάλι, ένα πλαίσιο για το m3 δημιουργείται στην κορυφή της στοίβας, όπως φαίνεται παρακάτω.

Java Στοίβα και Σωρός

Java Στοίβα και Σωρός

Τώρα ας υποθέσουμε ότι η μέθοδος m3 δημιουργεί ένα αντικείμενο για την κλάση Account, η οποία έχει δύο μεταβλητές παράδειγμα int p και int q.

class Account {
    int p;
    int q;
}

Εδώ είναι ο κώδικας για τη μέθοδο m3.

public void m3(){
    Account ref = new Account();
    // more code
}

Η εντολή new Account() δημιουργεί ένα αντικείμενο Account στο Heap.

Java Στοίβα και Σωρός

Η μεταβλητή αναφοράς ref δημιουργείται στη στοίβα μέσα στο πλαίσιο για το m3.

Java Στοίβα και Σωρός

Ο τελεστής ανάθεσης κάνει τη μεταβλητή αναφοράς να δείχνει προς το αντικείμενο στο Heap.

Java Στοίβα και Σωρός

Μόλις η μέθοδος ολοκληρώσει την εκτέλεση, ο έλεγχος επιστρέφει στη μέθοδο που την καλεί, η οποία σε αυτήν την περίπτωση είναι η μέθοδος m2.

Java Στοίβα και Σωρός

Το πλαίσιο για τη μέθοδο m3 αφαιρείται από τη στοίβα.

Java Στοίβα και Σωρός

Δεδομένου ότι η μεταβλητή αναφοράς δεν δείχνει πλέον στο αντικείμενο στο Heap, αυτό το αντικείμενο καθίσταται επιλέξιμο για συλλογή απορριμμάτων.

Java Στοίβα και Σωρός

Μόλις ολοκληρωθεί η μέθοδος m2, αφαιρείται από τη στοίβα και όλες οι μεταβλητές της παύουν να είναι διαθέσιμες. Το ίδιο συμβαίνει και με τη μέθοδο m1, και τελικά ο έλεγχος επιστρέφει στη μέθοδο main.

Τι γίνεται αν ένα αντικείμενο κρατάει μια άλλη αναφορά ως μεταβλητή παρουσίας του;

public static void main(String args[]) {
    A parent = new A();
    // more code
}
class A {
    B child = new B();
    int e;
}
class B {
    int c;
    int d;
}

Σε αυτήν την περίπτωση, η θυγατρική μεταβλητή αναφοράς βρίσκεται στο Heap ως μέρος του αντικειμένου Α και με τη σειρά της δείχνει στο δικό της αντικείμενο Β, όπως φαίνεται παρακάτω.

Java Στοίβα και Σωρός

Σε τι βρίσκεται το Garbage Collection Java?

Συλλογή σκουπιδιών σε Java είναι η διαδικασία με την οποία η JVM εκτελεί αυτόματα διαχείριση μνήμης. Το Garbage Collector βρίσκει αντικείμενα που δεν είναι πλέον προσβάσιμα από καμία ζωντανή αναφορά και ανακτά τη μνήμη τους. Η δυναμική κατανομή μνήμης γίνεται μέσω του νέου τελεστή και η μνήμη παραμένει κατανεμημένη μέχρι το πρόγραμμα να μην διατηρεί πλέον καμία αναφορά στο αντικείμενο.

Όταν δεν υπάρχουν πλέον αναφορές, το αντικείμενο θεωρείται ότι δεν χρειάζεται πλέον και η μνήμη που καταλαμβάνει μπορεί να ανακτηθεί. Δεν υπάρχει σαφής ανάγκη καταστροφής ενός αντικειμένου επειδή Java χειρίζεται την αποκομιδή αυτόματα μέσω του Συλλέκτη Απορριμμάτων.

Η τεχνική πίσω από αυτό είναι γνωστή ως Συλλογή απορριμάτωνΤα προγράμματα που δεν απελευθερώνουν μνήμη τελικά καταρρέουν όταν δεν απομένει τίποτα προς εκχώρηση. Τέτοια προγράμματα λέγεται ότι έχουν διαρροές μνήμης. Συλλογή σκουπιδιών σε Java εκτελείται αυτόματα καθ' όλη τη διάρκεια ζωής του προγράμματος, γεγονός που εξαλείφει το βάρος της χειροκίνητης αποδέσμευσης και μειώνει τον κίνδυνο διαρροών.

Στη C, αντίθετα, ο προγραμματιστής είναι υπεύθυνος για την απελευθέρωση μνήμης που έχει κατανεμηθεί δυναμικά μέσω της συνάρτησης free(). Εδώ είναι που Java Η διαχείριση μνήμης προσφέρει ένα ισχυρό πλεονέκτημα.

Τα σύγχρονα HotSpot JVM διαθέτουν πολλά Garbage Collectors, ρυθμισμένα για διαφορετικά φόρτα εργασίας. Το προεπιλεγμένο G1 Garbage Collector στοχεύει σε ισορροπημένη απόδοση και χρόνους παύσης σε σωρούς πολλών gigabyte. Τα ZGC και Shenandoah στοχεύουν σε παύσεις κάτω των χιλιοστών του δευτερολέπτου σε πολύ μεγάλους σωρούς, γεγονός που τα καθιστά...tracτικό για υπηρεσίες ευαίσθητες στην καθυστέρηση. Η επιλογή του σωστού συλλέκτη και η ρύθμιση των μεγεθών Young Generation, Old Generation και Metaspace είναι μια βασική δεξιότητα για Java έργο παράστασης.

Σημείωση: Όλα τα αντικείμενα δημιουργούνται στην ενότητα Heap της μνήμης, η οποία είναι η περιοχή που διαχειρίζεται το Garbage Collector.

Παράδειγμα: Για να μάθετε τον μηχανισμό συλλογής σκουπιδιών στο Java

Αυτή η αναλυτική παρουσίαση δείχνει πότε οι αναφορές γίνονται επιλέξιμες για τη Συλλογή Απορριμμάτων μέσα σε ένα απλό πρόγραμμα.

Βήμα 1) Αντιγράψτε τον παρακάτω κώδικα σε ένα πρόγραμμα επεξεργασίας.

class Student{
int a;
int b;
  public void setData(int c,int d){
    a=c;
    b=d;
  }
  public void showData(){
    System.out.println("Value of a = "+a);
    System.out.println("Value of b = "+b);
  }
  public static void main(String args[]){
    Student s1 = new Student();
    Student s2 = new Student();
    s1.setData(1,2);
    s2.setData(3,4);
    s1.showData();
    s2.showData();
    //Student s3;
    //s3=s2;
    //s3.showData();
    //s2=null;
    //s3.showData();
    //s3=null;
    //s3.showData();
  }
}

Βήμα 2) Αποθηκεύστε, μεταγλωττίστε και εκτελέστε τον κώδικα. Όπως φαίνεται στο διάγραμμα, δημιουργούνται δύο αντικείμενα και δύο μεταβλητές αναφοράς.

 Μηχανισμός Συλλεκτών Σκουπιδιών

Βήμα 3) Αποσχολιάστε τις γραμμές 20, 21 και 22. Αποθηκεύστε, μεταγλωττίστε και εκτελέστε τον κώδικα.

Βήμα 4) Όπως φαίνεται στο παρακάτω διάγραμμα, δύο μεταβλητές αναφοράς δείχνουν τώρα στο ίδιο αντικείμενο.

Μηχανισμός Συλλεκτών Σκουπιδιών

Βήμα 5) Αποσχολιάστε τις γραμμές 23 και 24. Αποθηκεύστε, μεταγλωττίστε και εκτελέστε τον κώδικα.

Βήμα 6) Όπως φαίνεται παρακάτω, το s2 γίνεται null, αλλά το s3 εξακολουθεί να δείχνει στο αντικείμενο, επομένως το αντικείμενο δεν είναι ακόμη επιλέξιμο για Συλλογή Απορριμμάτων.

 Μηχανισμός Συλλεκτών Σκουπιδιών

Βήμα 7) Αποσχολιάστε τις γραμμές 25 και 26. Αποθηκεύστε, μεταγλωττίστε και εκτελέστε τον κώδικα.

Βήμα 8) Σε αυτό το σημείο, δεν υπάρχουν αναφορές που να οδηγούν στο αντικείμενο, επομένως καθίσταται επιλέξιμο για τη Συλλογή Απορριμμάτων. Η Συλλέκτης Απορριμμάτων το αφαιρεί από τη μνήμη και δεν υπάρχει τρόπος να το ανακτήσει.

 Μάθετε Garbage Collector

Πώς να διαγράψετε ένα αντικείμενο σε Java?

Java δεν παρέχει τελεστή χειροκίνητης διαγραφής, επομένως η τυπική προσέγγιση είναι να αφαιρεθεί κάθε αναφορά στο αντικείμενο, ώστε το Garbage Collector να μπορεί να το ανακτήσει.

1) Για να κάνετε ένα αντικείμενο επιλέξιμο για Συλλογή Απορριμμάτων, αντιστοιχίστε κάθε μεταβλητή αναφοράς που δείχνει σε αυτό σε null.

2) Οι πρωτόγονοι τύποι δεν είναι αντικείμενα, επομένως δεν μπορούν να τους αντιστοιχιστεί τιμή null. Ο χώρος αποθήκευσης ανακτάται αυτόματα όταν εμφανίζεται το περιβάλλον πλαίσιο στοίβας.

Πώς να διαγράψετε ένα αντικείμενο σε Java

Συχνές Ερωτήσεις

Η στοίβα αποθηκεύει πλαίσια μεθόδων, τοπικές μεταβλητές και αναφορές με σειρά Last-In-First-Out ανά νήμα. Η στοίβα αποθηκεύει όλα τα αντικείμενα που δημιουργούνται με new και είναι κοινόχρηστη σε όλα τα νήματα, γι' αυτό και τη διαχειρίζεται το Garbage Collector.

Ο Συλλέκτης Απορριμμάτων (Garbage Collector) διαχειρίζεται αλυσίδες αναφοράς από τις ρίζες του GC, όπως τα ενεργά νήματα και τα στατικά πεδία. Οποιοδήποτε αντικείμενο που δεν μπορεί να προσεγγιστεί από μια ρίζα θεωρείται μη προσβάσιμο και καθίσταται επιλέξιμο για ανάκτηση κατά τον επόμενο κύκλο συλλογής.

Η Νεαρή Γενιά περιέχει βραχύβια αντικείμενα στους χώρους Eden και Survivor. Η Παλιά Γενιά περιέχει αντικείμενα που επιβιώνουν σε πολλαπλές συλλογές. Ο Μεταχώρος είναι μια περιοχή εγγενούς μνήμης που αποθηκεύει μεταδεδομένα κλάσης και αντικατέστησε την παλαιότερη PermGen στο Java 8.

Το G1 είναι η προεπιλεγμένη ρύθμιση και ταιριάζει στα περισσότερα φόρτα εργασίας σε σωρούς πολλών gigabyte. Τα ZGC και Shenandoah στοχεύουν σε παύσεις κάτω των χιλιοστών του δευτερολέπτου σε πολύ μεγάλους σωρούς και αποτελούν καλές επιλογές για υπηρεσίες που είναι ευαίσθητες στην καθυστέρηση και εκτελούνται σε Java 17 ή μεταγενέστερα.

Ορίστε κάθε αναφορά που δείχνει στο αντικείμενο σε null ή αφήστε τις αναφορές να βγουν εκτός πεδίου εφαρμογής όταν εμφανίζονται τα πλαίσια Stack τους. Μόλις καμία ενεργή αναφορά δεν φτάσει στο αντικείμενο, το Garbage Collector ανακτά τη μνήμη Heap.

Ναι. Τα εργαλεία συντονισμού που βασίζονται στην τεχνητή νοημοσύνη αναλύουν τα αρχεία καταγραφής GC, τους ρυθμούς κατανομής και τα ιστογράμματα παύσης για να προτείνουν μεγέθη σωρού, σημαίες συλλεκτών και αναλογίες Young Generation. Μειώνουν τον βρόχο ανατροφοδότησης σε σύγκριση με τον χειροκίνητο συντονισμό, ειδικά σε φόρτους εργασίας G1, ZGC και Shenandoah.

Οι πλατφόρμες παρατηρησιμότητας που βασίζονται στην τεχνητή νοημοσύνη απορροφούν δεδομένα heap dumps και ζωντανές μετρήσεις και, στη συνέχεια, ομαδοποιούν τα διατηρημένα γραφήματα αντικειμένων για να επισημαίνουν ύποπτες κλάσεις και αλυσίδες αναφοράς. Αυτό αναδεικνύει τους υποψηφίους που ενδέχεται να εμφανίσουν διαρροές ταχύτερα από τη χειροκίνητη ανάλυση heap και βοηθά τις ομάδες να εντοπίσουν την αιτία στην παραγωγή.

Συνοψίστε αυτήν την ανάρτηση με: