Κοινή χρήση τεχνολογίας

Δημοφιλές επιστημονικό άρθρο: Κατανοήστε την πραγματική μάχη του jvm σε ένα άρθρο (4) Σε βάθος κατανόηση της ανάλυσης διαφυγής Ανάλυση διαφυγής

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

ΣΦΑΙΡΙΚΗ ΕΙΚΟΝΑ

Τα αντικείμενα της Java εκχωρούνται στη μνήμη σωρού;

Εντάξει, είναι πολύ αφηρημένο. Ας δούμε πού εκχωρείται η μνήμη του παρακάτω αντικειμένου.

  1. public void test() {
  2. Object object = new Object();
  3. }
  4. 这个方法中的object对象,是在堆中分配内存么?

Πείτε το αποτέλεσμα: το αντικείμενο μπορεί να εκχωρήσει μνήμη στη στοίβα ή στο σωρό.

Εδώ είναι το βασικό σημείο: Κατά την υλοποίηση του JVM, προκειμένου να βελτιωθεί η απόδοση του JVM και να εξοικονομηθεί χώρος στη μνήμη, το JVM παρέχει μια δυνατότητα που ονομάζεται "ανάλυση διαφυγής" είναι μια σχετικά αιχμή τεχνολογία βελτιστοποίησης στο ρεύμα Java εικονική μηχανή, και είναι επίσης ένα JIT Μια πολύ σημαντική τεχνική βελτιστοποίησης. Το jdk6 άρχισε μόνο να εισάγει αυτήν την τεχνολογία, το jdk7 άρχισε να ενεργοποιεί την ανάλυση διαφυγής από προεπιλογή, το jdk8 άρχισε να βελτιώνει την ανάλυση διαφυγής και την ενεργοποίησε από προεπιλογή Μέχρι το JDK 9, η ανάλυση διαφυγής θα χρησιμοποιείται ως η προεπιλεγμένη μέθοδος βελτιστοποίησης και δεν υπάρχουν ειδικές παράμετροι μεταγλώττισης. απαιτούνται.

Τώρα κατανοήστε την πρόταση "αντικείμενο μπορεί να εκχωρήσει μνήμη στη στοίβα ή να εκχωρήσει μνήμη στη στοίβα". Μόνο η ανάλυση Escape έχει αρχίσει να υποστηρίζεται κατά πάσα πιθανότητα το jdk9 εκχωρείται στη στοίβα (το αντικείμενο εδώ είναι πολύ μικρό), επειδή το jdk9 υποστηρίζει και ενεργοποιεί μόνο την ανάλυση διαφυγής από προεπιλογή.

Με την ανάπτυξη των μεταγλωττιστών JIT (μεταγλωττιστές just-in-time) και τη σταδιακή ωρίμανση της τεχνολογίας ανάλυσης διαφυγής, η κατανομή στοίβας και η τεχνολογία βελτιστοποίησης αντικατάστασης κλιμακωτών θα κάνουν ότι "όλα τα αντικείμενα θα κατανεμηθούν στο σωρό" στη Java In στην εικονική μηχανή, τα αντικείμενα εκχωρούνται μνήμη στο σωρό, αλλά υπάρχει μια ειδική περίπτωση, δηλαδή, εάν μετά την ανάλυση διαφυγής διαπιστωθεί ότι ένα αντικείμενο δεν διαφεύγει από τη μέθοδο, μπορεί να βελτιστοποιηθεί για να εκχωρηθεί στη στοίβα. Όταν εκτελείται η μέθοδος Όταν ολοκληρωθεί, το πλαίσιο στοίβας ανοίγει και το αντικείμενο απελευθερώνεται. Αυτό εξαλείφει την ανάγκη εκχώρησης μνήμης στο σωρό και συλλογής σκουπιδιών.Το hotspot δεν το κάνει αυτήν τη στιγμήΑυτή είναι επίσης η πιο κοινή τεχνολογία αποθήκευσης εκτός σωρού.

Μετά την JDK 6u23 (απομνημόνευτη κύρια έκδοση JDK7), η ανάλυση διαφυγής είναι ενεργοποιημένη από προεπιλογή στο Hotspot, εάν χρησιμοποιείτε προηγούμενη έκδοση, μπορείτε να εμφανίσετε την ανάλυση διαφυγής μέσω της επιλογής "-XX:+DoEscapeAnalysis" -XX:+PrintEscapeAnalysis. Προβολή αποτελεσμάτων φίλτρου για ανάλυση διαφυγής.

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

Επιπλέον, σε βάθος προσαρμοσμένο με βάση το Open JDKTaoBao VM, μεταξύ των οποίων η καινοτόμος τεχνολογία GCIH (GC invisible heap) εφαρμόζει off-heap, κινούμενα αντικείμενα με μεγάλους κύκλους ζωής από το σωρό προς το εξωτερικό του σωρού και η GC δεν διαχειρίζεται αντικείμενα Java εντός του GCIH, μειώνοντας έτσι τη συχνότητα ανακύκλωσης GC και βελτιώνοντας τον σκοπό της αποτελεσματικότητας ανάκτησης GC.

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

σωρός:Όταν δημιουργείται ένα αντικείμενο, το αντικείμενο εκχωρείται στο σωρό και μια αναφορά στο σωρό ωθείται στη στοίβα.

διαφυγή:Όταν ένας δείκτης σε ένα αντικείμενο αναφέρεται με πολλές μεθόδους ή νήματα, λέμε ότι ο δείκτης διαφεύγει Γενικά, επιστρέφουν αντικείμενα και γενικές μεταβλητές.

Ανάλυση διαφυγής:Η μέθοδος που χρησιμοποιείται για την ανάλυση αυτού του φαινομένου διαφυγής ονομάζεται ανάλυση διαφυγής

Βελτιστοποίηση ανάλυσης διαφυγής - κατανομή στη στοίβα:Η κατανομή στη στοίβα σημαίνει ότι η παρουσία που δημιουργείται από την τοπική μεταβλητή στη μέθοδο (δεν εμφανίζεται διαφυγή) εκχωρείται στη στοίβα και δεν χρειάζεται να εκχωρηθεί στο σωρό Αφού ολοκληρωθεί η κατανομή, η εκτέλεση συνεχίζεται στη στοίβα κλήσης. Τέλος, το νήμα τελειώνει, ο χώρος στοίβας ανακυκλώνεται και τα τοπικά μεταβλητά αντικείμενα ανακυκλώνονται επίσης.

διαδικασία εκχώρησης μνήμης αντικειμένων java

  1. Εάν είναι ενεργοποιημένη η κατανομή στη στοίβα (ανάλυση διαφυγής), το JVM θα εκχωρήσει πρώτα στη στοίβα.
  2. Εάν η κατανομή στη στοίβα δεν είναι ενεργοποιημένη ή δεν πληρούνται οι προϋποθέσεις, θα εισαχθεί η κατανομή TLAB.
  3. Εάν η κατανομή TLAB είναι ανεπιτυχής ή ασυνεπής, προσδιορίστε εάν μπορεί να εισέλθει στην εκχώρηση παλιάς γενιάς.
  4. Εάν δεν μπορεί να εισέλθει στην παλιά γενιά, θα μπει στην κατανομή της Εδέμ.
  5. Δεν εκχωρούνται όλα τα αντικείμενα στο σωρό Εκτός από το σωρό, τα αντικείμενα μπορούν επίσης να εκχωρηθούν στη στοίβα και στο TLAB. (Τα περισσότερα αντικείμενα κατανέμονται στο σωρό)

Τα αντικείμενα στην Java εκχωρούνται απαραίτητα στο σωρό;

Απάντηση: Όχι απαραίτητα.

Εάν πληρούνται οι προϋποθέσεις της ανάλυσης διαφυγής, ένα αντικείμενο μπορεί να εκχωρηθεί στη στοίβα.Μειώστε την κατανομή μνήμης σωρού και την πίεση GC.Εφόσον η μνήμη στοίβας είναι περιορισμένη, εάν το αντικείμενο πληροί τις προϋποθέσεις για βαθμωτή αντικατάσταση,Εκτελείται μια περαιτέρω επέμβαση στο θέμα για να το σπάσει σε μέρη.Η συγκεκριμένη μέθοδος βαθμωτής αντικατάστασης είναι: το JVM θα διασπάσει περαιτέρω το αντικείμενο και θα αποσυνθέσει το αντικείμενο σε πολλές μεταβλητές μελών που χρησιμοποιούνται από αυτή τη μέθοδο.Έτσι, επιτυγχάνεται ο στόχος της καλύτερης χρήσης της μνήμης και των καταχωρητών στοίβας.

Το πώς να εκχωρήσετε αντικείμενα στο σωρό στη στοίβα απαιτεί ανάλυση διαφυγής.

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

Η βασική συμπεριφορά της ανάλυσης διαφυγής είναι η ανάλυση του δυναμικού εύρους των αντικειμένων:

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

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

Τι είναι η ανάλυση διαφυγής;

Σύντομη περιγραφή: "Ανάλυση διαφυγής: Μια στατική ανάλυση που καθορίζει το δυναμικό εύρος των δεικτών. Μπορεί να αναλύσει τα σημεία στο πρόγραμμα που μπορεί να προσπελαστεί ο δείκτης Στο πλαίσιο της έγκαιρης μεταγλώττισης του JVM, η ανάλυση διαφυγής θα καθορίσει εάν το νεοδημιουργημένο αντικείμενο διαφεύγει.

Η βάση για τη συλλογή just-in-time για να προσδιοριστεί εάν ένα αντικείμενο διαφεύγει: η μία είναι εάν το αντικείμενο είναι αποθηκευμένο στο σωρό (στατικό πεδίο ή πεδίο παρουσίας του αντικειμένου στο σωρό) και το άλλο είναι εάν το αντικείμενο μεταβιβάζεται σε άγνωστος κωδικός.

Το Escape Analysis είναι επί του παρόντος μια σχετικά προηγμένη τεχνολογία βελτιστοποίησης σε εικονικές μηχανές Java, όπως και η ανάλυση σχέσεων κληρονομικότητας τύπου, δεν είναι ένα μέσο άμεσης βελτιστοποίησης κώδικα, αλλά μια τεχνολογία ανάλυσης που παρέχει τη βάση για άλλα μέσα βελτιστοποίησης.

Ανάλυση διαφυγής: Είναι μια πολύ σημαντική τεχνολογία βελτιστοποίησης JIT, που χρησιμοποιείται για να καθοριστεί εάν το αντικείμενο θα έχει πρόσβαση εκτός της μεθόδου, δηλαδή για να ξεφύγει από το πεδίο εφαρμογής της μεθόδου. Η ανάλυση διαφυγής είναι ένα βήμα του μεταγλωττιστή JIT Μέσω του JIT μπορούμε να καθορίσουμε ποια αντικείμενα μπορούν να χρησιμοποιηθούν στο εσωτερικό της μεθόδου και δεν θα διαφύγουν προς τα έξω Ή πραγματοποιήστε αντικατάσταση βαθμωτών για να χωρίσετε ένα αντικείμενο σε πολλούς βασικούς τύπους για αποθήκευση. Είναι ένας αλγόριθμος ανάλυσης παγκόσμιας ροής δεδομένων πολλαπλών λειτουργιών που μπορεί να μειώσει αποτελεσματικά το φορτίο συγχρονισμού και την κατανομή σωρού μνήμης και την πίεση συλλογής σκουπιδιών σε προγράμματα Java. Μέσω της ανάλυσης διαφυγής, ο μεταγλωττιστής Java Hotspot μπορεί να αναλύσει το εύρος χρήσης της αναφοράς ενός νέου αντικειμένου και να αποφασίσει εάν θα εκχωρήσει αυτό το αντικείμενο στο σωρό.

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

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

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

  1. 关于逃逸分析的论文在1999年就已经发表,但直到Sun JDK 1.6才实现了逃逸分析,而且直到现在这项优化尚未足够成熟,仍有很大的改进余地。不成熟的原因主要是不能保证逃逸分析的性能收益必定高于它的消耗。如果要完全准确的判断一个对象是否会逃逸,需要进行数据流敏感的一系列复杂分析,从而确定程序各个分支执行时对此对象的影响。这是一个相对高耗时的过程,如果分析完后发现没有几个不逃逸的对象,那这些运行期耗用的时间就白白浪费了,所以目前虚拟机只能采用不那么准确,但时间压力相对较小的算法来完成逃逸分析。还有一点是,基于逃逸分析的一些优化手段,如上面提到的“栈上分配”,由于HotSpot虚拟机目前的实现方式导致栈上分配实现起来比较复杂,因此在HotSpot中暂时还没有做这项优化。
  2. 在测试结果中,实施逃逸分析后的程序在MicroBenchmarks中往往能运行出不错的成绩,但是在实际的应用程序,尤其是大型程序中反而发现实施逃逸分析可能出现效果不稳定的情况,或因分析过程耗时但却无法有效判别出非逃逸对象而导致性能(即使编译的收益)有所下降,所以在很长的一段时间里,即使是Server Compiler,也默认不开启逃逸分析(在JDK 1.6 Update 23的Server Compiler中才开始默认开启了逃逸分析),甚至在某些版本(如JDK 1.6 Update 18)中还曾经短暂的完全禁止了这项优化。
  3. 如果有需要,并且确认对程序运行有益,用户可以使用参数-XX:+DoEscapeAnalysis来手动开启逃逸分析,开启之后可以通过参数-XX:+PrintEscapeAnalysis来查看分析结果。有了逃逸分析支持之后,用户可以使用参数-XX:EliminateAllocations来开启标量替换,使用+XX:+EliminateLocks来开启同步消除,使用参数-XX:PrintEliminateAllocations查看标量的替换情况。
  4. 尽管目前逃逸分析的技术仍不是十分成熟,但是他却是即时编译器优化技术的一个重要的方向,在今后的虚拟机中,逃逸分析技术肯定会支撑起一系列使用有效的优化技术。

Βασικές αρχές ανάλυσης διαφυγής

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

Στο σύστημα μεταγλώττισης Java, η διαδικασία μετατροπής ενός αρχείου πηγαίου κώδικα Java σε μια εκτελέσιμη από υπολογιστή εντολή μηχανής απαιτεί δύο στάδια μεταγλώττισης:

Το πρώτο τμήμα της μεταγλώττισης αναφέρεται στον μεταγλωττιστή front-endαρχείο .javaμετατράπηκε σεαρχείο .class (αρχείο bytecode). Τα προϊόντα μεταγλωττιστή διεπαφής μπορεί να είναι το Javac της JDK ή ο αυξητικός μεταγλωττιστής στο Eclipse JDT.

Στο δεύτερο στάδιο μεταγλώττισης, το JVM ερμηνεύει τον bytecode και τον μεταφράζει σε αντίστοιχες οδηγίες μηχανής, διαβάζει στον bytecode έναν προς έναν και τον ερμηνεύει και τον μεταφράζει σε κώδικα μηχανής ένα προς ένα.

Προφανώς, λόγω της ενδιάμεσης διαδικασίας ερμηνείας, η ταχύτητα εκτέλεσής του θα είναι αναπόφευκτα πολύ πιο αργή από αυτή ενός εκτελέσιμου προγράμματος δυαδικού bytecode. Αυτή είναι η λειτουργία του παραδοσιακού διερμηνέα JVM (Interpreter).

Πώς να κόψετε τους μεσάζοντες και να βελτιώσετε την αποτελεσματικότητα;

Για να λυθεί αυτό το πρόβλημα απόδοσης, εισήχθη η τεχνολογία JIT (Just In Time Compiler).

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

JIT Compiler (Just-in-timeCompiler) μεταγλώττιση ακριβώς στην ώρα. Η παλαιότερη λύση υλοποίησης Java αποτελούνταν από ένα σύνολο μεταφραστών (διερμηνέων) που μετέφραζαν κάθε εντολή Java σε μια αντίστοιχη εντολή μικροεπεξεργαστή και τις εκτελούσαν διαδοχικά σύμφωνα με τη σειρά των μεταφρασμένων εντολών, επειδή μια εντολή Java μπορούσε να Μεταφραστεί σε μια ντουζίνα ή δεκάδες ισοδύναμες οδηγίες μικροεπεξεργαστή, αυτή η λειτουργία εκτελείται πολύ αργά.

Πώς να αφαιρέσετε μερικώς τους ενδιάμεσους συνδέσμους;

Όταν το JVM διαπιστώσει ότι μια συγκεκριμένη μέθοδος ή μπλοκ κώδικα εκτελείται ιδιαίτερα συχνά, θα θεωρήσει ότι είναι "Κωδικός Hot Spot". Στη συνέχεια, το JIT θα μεταφράσει μέρος του "hot code" σε κώδικα μηχανής που σχετίζεται με το τοπικό μηχάνημα, θα το βελτιστοποιήσει και στη συνέχεια θα αποθηκεύσει προσωρινά τον μεταφρασμένο κώδικα μηχανής για επόμενη χρήση.

Πού να αποθηκεύσετε προσωρινά τον μεταφρασμένο κώδικα μηχανής; Αυτή η κρυφή μνήμη ονομάζεται προσωρινή μνήμη κώδικα. Μπορεί να φανεί ότι οι μέθοδοι για την επίτευξη υψηλής ταυτότητος μεταξύ των εφαρμογών JVM και WEB είναι παρόμοιες και εξακολουθούν να χρησιμοποιούν αρχιτεκτονική κρυφής μνήμης.

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

Ως εκ τούτου, ο γενικός στόχος του JIT είναι να ανακαλύψει τον καυτό κώδικα και ο καυτός κώδικας έχει γίνει το κλειδί για τη βελτίωση της απόδοσης. Έτσι προέκυψε το όνομα hotspot JVM.

Επομένως, η συνολική στρατηγική της JVM είναι:

  • Για τους περισσότερους από τους ασυνήθιστους κώδικες, δεν χρειάζεται να ξοδεύουμε χρόνο για να τους μεταγλωττίσουμε σε κώδικα μηχανής, αλλά να τους εκτελούμε μέσω ερμηνείας και εκτέλεσης.

  • Από την άλλη πλευρά, για ζεστό κώδικα που καταλαμβάνει μόνο ένα μικρό μέρος, μπορούμε να τον μεταγλωττίσουμε σε κώδικα μηχανής για να επιτύχουμε την ιδανική ταχύτητα λειτουργίας.

Η εμφάνιση του JIT (just in time compilation) και η διαφορά μεταξύ των διερμηνέων

(1) Ο διερμηνέας ερμηνεύει τον bytecode σε κώδικα μηχανής Ακόμα κι αν συναντήσει τον ίδιο bytecode την επόμενη φορά, θα συνεχίσει να εκτελεί επαναλαμβανόμενη ερμηνεία.

(2) Το JIT μεταγλωττίζει ορισμένους bytecode σε κώδικες μηχανής και τους αποθηκεύει στην προσωρινή μνήμη κώδικα Όταν συναντήσετε τον ίδιο κώδικα την επόμενη φορά, θα εκτελεστεί απευθείας χωρίς να μεταγλωττιστεί ξανά.

(3) Ο διερμηνέας ερμηνεύει τον bytecode σε κώδικα μηχανής που είναι κοινός σε όλες τις πλατφόρμες.

(4) Το JIT θα δημιουργήσει κώδικα μηχανής για συγκεκριμένη πλατφόρμα με βάση τον τύπο πλατφόρμας.

Το JVM περιέχει πολλούς μεταγλωττιστές just-in-time, κυρίως C1 και C2, και Graal (πειραματικό).

Πολλαπλοί μεταγλωττιστές just-in-time θα βελτιστοποιήσουν τον bytecode και θα δημιουργήσουν κώδικα μηχανής

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

Το JVM διαιρεί την κατάσταση εκτέλεσης σε 5 επίπεδα:

  • Επίπεδο 0, Διερμηνέας

  • Επίπεδο 1, μεταγλώττιση και εκτέλεση με χρήση μεταγλωττιστή C1 just-in-time (χωρίς προφίλ)

  • Επίπεδο 2, μεταγλώττιση και εκτέλεση με χρήση μεταγλωττιστή C1 just-in-time (με βασικό προφίλ)

  • Επίπεδο 3, μεταγλώττιση και εκτέλεση με χρήση μεταγλωττιστή C1 just-in-time (με πλήρες προφίλ)

  • Επίπεδο 4, μεταγλωττίζεται και εκτελείται με χρήση μεταγλωττιστή C2 just-in-time

Το JVM δεν θα ενεργοποιήσει απευθείας το C2 Αντίθετα, συλλέγει πρώτα την κατάσταση λειτουργίας του προγράμματος μέσω της μεταγλώττισης C1 και στη συνέχεια καθορίζει εάν θα ενεργοποιηθεί το C2 με βάση τα αποτελέσματα της ανάλυσης.

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

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

Ο σκοπός ορισμένων από τις βελτιστοποιήσεις είναι ναΜειώστε την πίεση εκχώρησης σωρού μνήμης , μια από τις σημαντικές τεχνικές στη βελτιστοποίηση JIT ονομάζεται ανάλυση διαφυγής. Σύμφωνα με την ανάλυση διαφυγής, ο μεταγλωττιστής just-in-time θα βελτιστοποιήσει τον κώδικα ως εξής κατά τη διαδικασία μεταγλώττισης:

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

1. Η στατική ανάλυση είναι ανάλυση που εκτελείται κατά το χρόνο μεταγλώττισης

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

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

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

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

Κατάσταση διαφυγής: καθολική διαφυγή, διαφυγή παραμέτρων, χωρίς διαφυγή

Ένα αντικείμενο έχει τρεις καταστάσεις διαφυγής: καθολική διαφυγή, διαφυγή παραμέτρων και καμία διαφυγή.

παγκόσμια απόδραση(GlobalEscape): Δηλαδή, το εύρος ενός αντικειμένου διαφεύγει από την τρέχουσα μέθοδο ή το τρέχον νήμα.

Γενικά υπάρχουν τα ακόλουθα σενάρια:
① Το αντικείμενο είναι μια στατική μεταβλητή
② Το αντικείμενο είναι ένα αντικείμενο που έχει διαφύγει
③ Το αντικείμενο χρησιμοποιείται ως η επιστρεφόμενη τιμή της τρέχουσας μεθόδου

Διαφυγή παραμέτρων(ArgEscape): Δηλαδή, ένα αντικείμενο μεταβιβάζεται ως παράμετρος μεθόδου ή αναφέρεται από μια παράμετρο, αλλά δεν εμφανίζεται καθολική διαφυγή κατά τη διαδικασία κλήσης Αυτή η κατάσταση καθορίζεται από τον bytecode της καλούμενης μεθόδου.

δεν υπάρχει διαφυγή: Δηλαδή, το αντικείμενο στη μέθοδο δεν διαφεύγει.

Ο κωδικός του δείγματος κατάστασης διαφυγής είναι ο εξής:

  1. public class EscapeAnalysisTest {
  2. public static Object globalVariableObject;
  3. public Object instanceObject;
  4. public void globalVariableEscape(){
  5. globalVariableObject = new Object(); // 静态变量,外部线程可见,发生逃逸
  6. }
  7. public void instanceObjectEscape(){
  8. instanceObject = new Object(); // 赋值给堆中实例字段,外部线程可见,发生逃逸
  9. }
  10. public Object returnObjectEscape(){
  11. return new Object(); // 返回实例,外部线程可见,发生逃逸
  12. }
  13. public void noEscape(){
  14. Object noEscape = new Object(); // 仅创建线程可见,对象无逃逸
  15. }
  16. }

Τρόποι διαφυγής: διαφυγή μεθόδου και διαφυγή νήματος


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

Οι διαφυγές μεθόδου περιλαμβάνουν:

  • Περάστε τη διεύθυνση αντικειμένου σε άλλες μεθόδους καλώντας παραμέτρους,
  • Το αντικείμενο επιστρέφει τον δείκτη αντικειμένου σε άλλες μεθόδους μέσω της δήλωσης επιστροφής
  • και τα λοιπά.
  1. 我们可以用下面的代码来表示这个现象。
  2. //StringBuffer对象发生了方法逃逸
  3. public static StringBuffer createStringBuffer(String s1, String s2) {
  4. StringBuffer sb = new StringBuffer();
  5. sb.append(s1);
  6. sb.append(s2);
  7. return sb;
  8. }
  9. 上面的例子中,StringBuffer 对象通过return语句返回。
  10. StringBuffer sb是一个方法内部变量,上述代码中直接将sb返回,这样这个StringBuffer有可能被其他方法所改变,这样它的作用域就不只是在方法内部,虽然它是一个局部变量,称其逃逸到了方法外部。
  11. 甚至还有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸。
  12. 不直接返回 StringBuffer,那么StringBuffer将不会逃逸出方法。
  13. 具体的代码如下:
  14. // 非方法逃逸
  15. public static String createString(String s1, String s2) {
  16. StringBuffer sb = new StringBuffer();
  17. sb.append(s1);
  18. sb.append(s2);
  19. return sb.toString();
  20. }
  21. 可以看出,想要逃逸方法的话,需要让对象本身被外部调用,或者说, 对象的指针,传递到了 方法之外。

Πώς να προσδιορίσετε γρήγορα εάν έχει πραγματοποιηθεί ανάλυση διαφυγής Ας δούμε αν η νέα οντότητα αντικειμένου καλείται εκτός της μεθόδου;

  1. public class EscapeAnalysis {
  2.  
  3.     public EscapeAnalysis obj;
  4.  
  5.     /**
  6.      * 方法返回EscapeAnalysis对象,发生逃逸
  7.      * @return
  8.      */
  9.     public EscapeAnalysis getInstance() {
  10.         return obj == null ? new EscapeAnalysis():obj;
  11.     }
  12.  
  13.     /**
  14.      * 为成员属性赋值,发生逃逸
  15.      */
  16.     public void setObj() {
  17.         this.obj = new EscapeAnalysis();
  18.     }
  19.  
  20.     /**
  21.      * 对象的作用于仅在当前方法中有效,没有发生逃逸
  22.      */
  23.     public void useEscapeAnalysis() {
  24.         EscapeAnalysis e = new EscapeAnalysis();
  25.     }
  26.  
  27.     /**
  28.      * 引用成员变量的值,发生逃逸
  29.      */
  30.     public void useEscapeAnalysis2() {
  31.         EscapeAnalysis e = getInstance();
  32.     }
  33. }

2. Διαφυγή νήματος: Αυτό το αντικείμενο είναι προσβάσιμο από άλλα νήματα, όπως εκχωρείται σε μια μεταβλητή παρουσίας και προσπελάζεται από άλλα νήματα. Το αντικείμενο ξέφυγε από το τρέχον νήμα.

Στρατηγικές βελτιστοποίησης για ανάλυση διαφυγής

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

Παράμετροι που σχετίζονται με την ανάλυση διαφυγής:

  1. -XX:+DoEscapeAnalysis 开启逃逸分析
  2. -XX:+PrintEscapeAnalysis 开启逃逸分析后,可通过此参数查看分析结果。
  3. -XX:+EliminateAllocations 开启标量替换
  4. -XX:+EliminateLocks 开启同步消除
  5. -XX:+PrintEliminateAllocations 开启标量替换后,查看标量替换情况。

1. Κατανομή στοίβας

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

Υπό κανονικές συνθήκες, τα αντικείμενα που δεν μπορούν να διαφύγουν καταλαμβάνουν σχετικά μεγάλο χώρο Εάν μπορεί να χρησιμοποιηθεί ο χώρος στη στοίβα, ένας μεγάλος αριθμός αντικειμένων θα καταστραφεί όταν τελειώσει η μέθοδος, μειώνοντας την πίεση GC.

        Ιδέες κατανομής στη στοίβαΗ κατανομή στη στοίβα είναι μια τεχνολογία βελτιστοποίησης που παρέχεται από το JVM.
Η ιδέα είναι:

  1. Για ιδιωτικά αντικείμενα νήματος (αντικείμενα στα οποία δεν είναι δυνατή η πρόσβαση από άλλα νήματα), μπορούν να εκχωρηθούν στη μνήμη στοίβας αντί για τη μνήμη σωρού, η οποία είναι μια λύση για την αντικατάσταση συγκεντρωτικών μεταβλητών με βαθμωτές.
  2. Το πλεονέκτημα της κατανομής στη στοίβα είναι ότι μπορεί να καταστραφεί αυτόματα μετά το τέλος της μεθόδου, χωρίς να απαιτείται παρέμβαση GC, βελτιώνοντας την απόδοση του συστήματος.
  3. Για μεγάλο αριθμό διάσπαρτων αντικειμένων, η κατανομή στη στοίβα παρέχει μια καλή στρατηγική κατανομής αντικειμένων Η κατανομή των μπλοκ ταχύτητας στη στοίβα μπορεί να αποφύγει αποτελεσματικά τον αρνητικό αντίκτυπο της ανακύκλωσης GC.

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

  1. 开启逃逸分析:-XX:+DoEscapeAnalysis
  2. 关闭逃逸分析:-XX:-DoEscapeAnalysis
  3. 显示分析结果:-XX:+PrintEscapeAnalysis
  4. 开启标量替换:-XX:+EliminateAllocations
  5. 关闭标量替换:-XX:-EliminateAllocations
  6. 显示标量替换详情:-XX:+PrintEliminateAllocations

Παράδειγμα κατανομής στη στοίβα:

  1. 示例1
  2. import java.lang.management.ManagementFactory;
  3. import java.util.List;
  4. /**
  5. * 逃逸分析优化-栈上分配
  6. * 栈上分配,意思是方法内局部变量(未发生逃逸)生成的实例在栈上分配,不用在堆中分配,分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量对象也被回收。
  7. * 一般生成的实例都是放在堆中的,然后把实例的指针或引用压入栈中。
  8. *虚拟机参数设置如下,表示做了逃逸分析 消耗时间在10毫秒以下
  9. * -server -Xmx10M -Xms10M
  10. -XX:+DoEscapeAnalysis -XX:+PrintGC
  11. *
  12. *虚拟机参数设置如下,表示没有做逃逸分析 消耗时间在1000毫秒以上
  13. * -server -Xmx10m -Xms10m
  14. -XX: -DoEscapeAnalysis -XX:+PrintGC
  15. * @author 734621
  16. *
  17. */
  18. public class OnStack{
  19. public static void alloc(){
  20. byte[] b=new byte[2];
  21. b[0]=1;
  22. }
  23. public static void main(String [] args){
  24. long b=System.currentTimeMillis();
  25. for(int i=0;i<100000000;i++){
  26. alloc();
  27. }
  28. long e=System.currentTimeMillis();
  29. System.out.println("消耗时间为:" + (e - b));
  30. List<String> paramters = ManagementFactory.getRuntimeMXBean().getInputArguments();
  31. for(String p : paramters){
  32. System.out.println(p);
  33. }
  34. }
  35. }
  36. 加逃逸分析的结果
  37. [GC (Allocation Failure) 2816K->484K(9984K), 0.0013117 secs]
  38. 消耗时间为:7
  39. -Xmx10m
  40. -Xms10m
  41. -XX:+DoEscapeAnalysis
  42. -XX:+PrintGC
  43. 没有加逃逸分析的结果如下:
  44. [GC (Allocation Failure) 3320K->504K(9984K), 0.0003174 secs]
  45. [GC (Allocation Failure) 3320K->504K(9984K), 0.0002524 secs]
  46. 消耗时间为:1150
  47. -Xmx10m
  48. -Xms10m
  49. -XX:-DoEscapeAnalysis
  50. -XX:+PrintGC
  51. 以上测试可以看出,栈上分配可以明显提高效率: 效率是不开启的1150/7= 160
  52. 示例2
  53. 我们通过举例来说明 开启逃逸分析 和 未开启逃逸分析时候的情况
  54. class User {
  55. private String name;
  56. private String age;
  57. private String gender;
  58. private String phone;
  59. }
  60. public class StackAllocation {
  61. public static void main(String[] args) throws InterruptedException {
  62. long start = System.currentTimeMillis();
  63. for (int i = 0; i < 100000000; i++) {
  64. alloc();
  65. }
  66. long end = System.currentTimeMillis();
  67. System.out.println("花费的时间为:" + (end - start) + " ms");
  68. // 为了方便查看堆内存中对象个数,线程sleep
  69. Thread.sleep(10000000);
  70. }
  71. private static void alloc() {
  72. // 未发生逃逸
  73. User user = new User();
  74. }
  75. }
  76. 设置JVM参数,表示未开启逃逸分析
  77. -Xmx1G -Xms1G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails
  78. 花费的时间为:664 ms
  79. 然后查看内存的情况,发现有大量的User存储在堆中
  80. 开启逃逸分析
  81. -Xmx1G -Xms1G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails
  82. 然后查看运行时间,我们能够发现花费的时间快速减少,同时不会发生GC操作
  83. 花费的时间为:5 ms
  84. 在看内存情况,我们发现只有很少的User对象,说明User未发生逃逸,因为它存储在栈中,随着栈的销毁而消失。

Συγκριτικά, μπορούμε να δούμε

  • Ενεργοποιήστε την κατανομή στοίβας και εκχωρήστε αντικείμενα χωρίς διαφυγή στη μνήμη στοίβας, η οποία προφανώς θα εκτελείται πιο αποτελεσματικά.
  • Μετά το κλείσιμο της κατανομής στη στοίβα, το GC εκτελεί συχνά συλλογή σκουπιδιών.

2. Εξάλειψη κλειδώματος

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

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

  1. -XX:+EliminateLocks #开启同步锁消除(JVM默认状态)
  2. -XX:-EliminateLocks #关闭同步锁消除
  1. 通过示例: 明显可以看到“逃逸分析和锁消除” 对性能的提升
  2. public void testLock(){
  3. long t1 = System.currentTimeMillis();
  4. for (int i = 0; i < 100_000_000; i++) {
  5. locketMethod();
  6. }
  7. long t2 = System.currentTimeMillis();
  8. System.out.println("耗时:"+(t2-t1));
  9. }
  10. public static void locketMethod(){
  11. EscapeAnalysis escapeAnalysis = new EscapeAnalysis();
  12. synchronized(escapeAnalysis) {
  13. escapeAnalysis.obj2="abcdefg";
  14. }
  15. }
  16. 设置JVM参数,开启逃逸分析, 耗时:
  17. java -Xmx64m -Xms64m -XX:+DoEscapeAnalysis
  18. 设置JVM参数,关闭逃逸分析, 耗时:
  19. java -Xmx64m -Xms64m -XX:-DoEscapeAnalysis
  20. 设置JVM参数,关闭锁消除,再次运行
  21. java -Xmx64m -Xms15m -XX:+DoEscapeAnalysis -XX:-EliminateLocks
  22. 设置JVM参数,开启锁消除,再次运行
  23. java -Xmx64m -Xms15m -XX:+DoEscapeAnalysis -XX:+EliminateLocks

Το κόστος του συγχρονισμού νημάτων είναι αρκετά υψηλό και η συνέπεια του συγχρονισμού είναι η μειωμένη συγχρονισμός και η απόδοση.

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

  1. 例如下面的代码
  2. public void f() {
  3.     Object hellis = new Object();
  4.     synchronized(hellis) {
  5.         System.out.println(hellis);
  6.     }
  7. }
  8. 代码中对hellis这个对象加锁,但是hellis对象的生命周期只在f()方法中,并不会被其他线程所访问到,所以在JIT编译阶段就会被优化掉,优化成:
  9. public void f() {
  10.     Object hellis = new Object();
  11.     System.out.println(hellis);
  12. }
  13. 我们将其转换成字节码,此处发现,还是有同步锁的身影,是因为优化是在编译阶段的,在加载进内存后发生。

3. Αντικατάσταση βαθμωτών

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

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

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

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

Η αντικατάσταση βαθμίδας είναι επίσης ενεργοποιημένη από προεπιλογή στο JDK1.8, αλλά πρέπει επίσης να βασίζεται στην ενεργοποίηση της ανάλυσης διαφυγής.

Ένα βαθμωτό είναι ένα στοιχείο που δεν μπορεί να αναλυθεί σε μικρότερα δεδομένα. Ο πρωτόγονος τύπος δεδομένων στην Java είναι βαθμωτός.

Αντίθετα, τα δεδομένα που μπορούν να αποσυντεθούν ονομάζονται συγκεντρωτικά στοιχεία.

  1. public static void main(String args[]) {
  2.     alloc();
  3. }
  4. class Point {
  5.     private int x;
  6.     private int y;
  7. }
  8. private static void alloc() {
  9.     Point point = new Point(1,2);
  10.     System.out.println("point.x" + point.x + ";point.y" + point.y);
  11. }
  12. 以上代码,经过标量替换后,就会变成
  13. private static void alloc() {
  14.     int x = 1;
  15.     int y = 2;
  16.     System.out.println("point.x = " + x + "; point.y=" + y);
  17. }

Στο στάδιο JIT, εάν διαπιστωθεί μέσω της ανάλυσης διαφυγής ότι ένα αντικείμενο δεν θα είναι προσβάσιμο από τον έξω κόσμο, τότε μετά τη βελτιστοποίηση JIT, το αντικείμενο θα αποσυναρμολογηθεί σε πολλές μεταβλητές μελών που περιέχονται σε αυτό και θα αντικατασταθεί. Αυτή η διαδικασία είναι βαθμωτή αντικατάσταση.
Μπορεί να φανεί ότι μετά από ανάλυση διαφυγής, διαπιστώθηκε ότι η συνολική ποσότητα Point δεν διέφυγε, οπότε αντικαταστάθηκε από δύο βαθμωτές. Ποια είναι λοιπόν τα οφέλη της βαθμωτής αντικατάστασης; Δηλαδή, μπορεί να μειώσει σημαντικά τη χρήση της μνήμης σωρού. Επειδή από τη στιγμή που δεν υπάρχει ανάγκη δημιουργίας αντικειμένων, δεν υπάρχει ανάγκη να εκχωρηθεί μνήμη σωρού. Η βαθμωτή αντικατάσταση παρέχει μια καλή βάση για την κατανομή στη στοίβα.

Δοκιμή ανάλυσης διαφυγής

  1. 逃逸分析测试
  2. 代码如下,大致思路就是 for 循环 1 亿次,循环体内调用外部的 allot() 方法,而 allot() 方法的作用就是简单创建一个对象,但是这个对象是内部的,所以是未逃逸的,所以理论上 JVM 是会进行优化的,我们拭目以待。并且我们会对比开启和关闭逃逸分析之后各自程序的运行时间:
  3. /**
  4. * @ClassName: EscapeAnalysisTest
  5. * @Description: http://www.jetchen.cn 逃逸分析 demo
  6. * @Author: Jet.Chen
  7. * @Date: 2020/11/23 14:26
  8. * @Version: 1.0
  9. **/
  10. public class EscapeAnalysisTest {
  11. public static void main(String[] args) {
  12. long t1 = System.currentTimeMillis();
  13. for (int i = 0; i < 100000000; i++) {
  14. allot();
  15. }
  16. long t2 = System.currentTimeMillis();
  17. System.out.println(t2-t1);
  18. }
  19. private static void allot() {
  20. Jet jet = new Jet();
  21. }
  22. static class Jet {
  23. public String name;
  24. }
  25. }
  26. 上面就是我们进行逃逸分析测试的代码, mian() 方法末尾有一个线程暂停,目的是为了观察此时 JVM 中的内存情况。
  27. Step 1:测试开启逃逸
  28. 由于环境是 jdk1.8,默认开启了逃逸分析,所以直接运行,得到结果如下,程序耗时 3 毫秒:
  29. 此时线程是处于睡眠状态的,我们观察下内存情况,发现堆内存中一共新建了 11 万个 Jet 对象。
  30. Step 2:测试关闭逃逸
  31. 我们关闭逃逸分析再来运行一次(使用 java -XX:-DoEscapeAnalysis EscapeAnalysisTest 来运行代码即可),得到结果如下,程序耗时 400 毫秒:
  32. 此时我们观察下内存情况,发现堆内存中一共新建了 3 千多万个 Jet 对象。
  33. 所以,无论是从代码的执行时间(3 毫秒 VS 400 毫秒),还是从堆内存中对象的数量(11 万个 VS 3 千万个)来分析,在上述场景下,开启逃逸分析是有正向益的。
  34. Step 3:测试标量替换
  35. 我们测试下开启和关闭 标量替换,如下图:
  36. 由上图我们可以看出,在上述极端场景下,开启和关闭标量替换对于性能的影响也是满巨大的,另外,同时也验证了标量替换功能生效的前提是逃逸分析已经开启,否则没有意义。
  37. Step 4:测试锁消除
  38. 测试锁消除,我们需要简单调整下代码,即给 allot() 方法中的内容加锁处理,如下:
  39. private static void allot() {
  40. Jet jet = new Jet();
  41. synchronized (jet) {
  42. jet.name = "jet Chen";
  43. }
  44. }
  45. 然后我们运行测试代码,测试结果也很明显,在上述场景下,开启和关闭锁消除对程序性能的影响也是巨大的。
  46. /**
  47. * 进行两种测试
  48. * 关闭逃逸分析,同时调大堆空间,避免堆内GC的发生,如果有GC信息将会被打印出来
  49. * VM运行参数:-Xmx4G -Xms4G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
  50. *
  51. * 开启逃逸分析 jdk8默认开启
  52. * VM运行参数:-Xmx4G -Xms4G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
  53. *
  54. * 执行main方法后
  55. * jps 查看进程
  56. * jmap -histo 进程ID
  57. *
  58. */
  59. @Slf4j
  60. public class EscapeTest {
  61. public static void main(String[] args) {
  62. long start = System.currentTimeMillis();
  63. for (int i = 0; i < 500000; i++) {
  64. alloc();
  65. }
  66. long end = System.currentTimeMillis();
  67. log.info("执行时间:" + (end - start) + " ms");
  68. try {
  69. Thread.sleep(Integer.MAX_VALUE);
  70. } catch (InterruptedException e1) {
  71. e1.printStackTrace();
  72. }
  73. }
  74. /**
  75. * JIT编译时会对代码进行逃逸分析
  76. * 并不是所有对象存放在堆区,有的一部分存在线程栈空间
  77. * Ponit没有逃逸
  78. */
  79. private static String alloc() {
  80. Point point = new Point();
  81. return point.toString();
  82. }
  83. /**
  84. *同步省略(锁消除) JIT编译阶段优化,JIT经过逃逸分析之后发现无线程安全问题,就会做锁消除
  85. */
  86. public void append(String str1, String str2) {
  87. StringBuffer stringBuffer = new StringBuffer();
  88. stringBuffer.append(str1).append(str2);
  89. }
  90. /**
  91. * 标量替换
  92. *
  93. */
  94. private static void test2() {
  95. Point point = new Point(1,2);
  96. System.out.println("point.x="+point.getX()+"; point.y="+point.getY());
  97. // int x=1;
  98. // int y=2;
  99. // System.out.println("point.x="+x+"; point.y="+y);
  100. }
  101. }
  102. @Data
  103. @AllArgsConstructor
  104. @NoArgsConstructor
  105. class Point{
  106. private int x;
  107. private int y;
  108. }

4. Μέθοδος Inlining

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

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

Πρακτικά σενάρια εφαρμογής

Η ανάλυση διαφυγής έχει ένα ευρύ φάσμα σεναρίων εφαρμογών σε πραγματικές εφαρμογές Java Τα ακόλουθα είναι μερικά κοινά σενάρια εφαρμογών:

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

Μειονεκτήματα της ανάλυσης διαφυγής

Η εργασία για την ανάλυση διαφυγής δημοσιεύτηκε το 1999, αλλά δεν εφαρμόστηκε μέχρι το JDK1.6 και αυτή η τεχνολογία δεν είναι ακόμη πολύ ώριμη.

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

Ένα ακραίο παράδειγμα είναι ότι μετά την ανάλυση διαφυγής, διαπιστώνεται ότι κανένα αντικείμενο δεν διαφεύγει. Τότε η διαδικασία της ανάλυσης διαφυγής πάει χαμένη.

Παρόλο που αυτή η τεχνολογία δεν είναι πολύ ώριμη, είναι επίσης ένα πολύ σημαντικό μέσο στην τεχνολογία βελτιστοποίησης μεταγλωττιστή έγκαιρα. Παρατήρησα ότι υπάρχουν κάποιες απόψεις ότι μέσω της ανάλυσης διαφυγής, το JVM θα εκχωρήσει αντικείμενα στη στοίβα που δεν θα διαφύγουν. Αυτό είναι θεωρητικά δυνατό, αλλά εξαρτάται από την επιλογή του σχεδιαστή JvM. Από όσο ξέρω, το Oracle Hotspot JVM δεν το κάνει αυτό.

Προς το παρόν, πολλά βιβλία βασίζονται ακόμη σε εκδόσεις πριν από το JDK 7 υποβλήθηκαν σε μεγάλες αλλαγές. Ωστόσο, η κρυφή μνήμη προσωρινής συμβολοσειράς και οι στατικές μεταβλητές δεν μεταφέρονται στην περιοχή μεταδεδομένων, αλλά εκχωρούνται απευθείας στο σωρό, επομένως αυτό είναι επίσης συνεπές με το συμπέρασμα του προηγούμενου σημείου: οι στιγμιότυπα αντικειμένων εκχωρούνται στο σωρό. Το παραπάνω παράδειγμα επιταχύνεται λόγω της βαθμωτής αντικατάστασης.

Οφέλη από την ανάλυση διαφυγής


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

Κατανομή στη στοίβα:


Υπό κανονικές συνθήκες, τα αντικείμενα που δεν μπορούν να διαφύγουν καταλαμβάνουν σχετικά μεγάλο χώρο Εάν μπορεί να χρησιμοποιηθεί ο χώρος στη στοίβα, ένας μεγάλος αριθμός αντικειμένων θα καταστραφεί όταν τελειώσει η μέθοδος, μειώνοντας την πίεση GC.


Σύγχρονη εξάλειψη:


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


Κλιμωτή αντικατάσταση:


Οι πρωτόγονοι τύποι δεδομένων στην εικονική μηχανή Java (αριθμητικοί τύποι όπως τύποι int, long και αναφοράς κ.λπ.) δεν μπορούν να αποσυντεθούν περαιτέρω και μπορούν να ονομαστούν βαθμωτοί. Αντίθετα, εάν ένα κομμάτι δεδομένων μπορεί να συνεχίσει να αποσυντίθεται, ονομάζεται άθροισμα Το πιο τυπικό σύνολο στην Java είναι ένα αντικείμενο. Εάν η ανάλυση διαφυγής αποδείξει ότι ένα αντικείμενο δεν είναι προσβάσιμο εξωτερικά και ότι το αντικείμενο μπορεί να αποσυντεθεί, το αντικείμενο μπορεί να μην δημιουργηθεί όταν εκτελείται πραγματικά το πρόγραμμα, αλλά να δημιουργήσει απευθείας πολλές από τις μεταβλητές μελών του που χρησιμοποιούνται από αυτήν τη μέθοδο. Οι αποσυναρμολογημένες μεταβλητές μπορούν να αναλυθούν και να βελτιστοποιηθούν χωριστά Αφού ισοπεδωθούν τα χαρακτηριστικά, δεν χρειάζεται να δημιουργηθούν σχέσεις μέσω δεικτών αναφοράς, μπορούν να αποθηκευτούν συνεχώς και συμπαγώς, κάτι που είναι πιο φιλικό σε διάφορες αποθήκες και εξοικονομεί μεγάλο μέρος του χειρισμού δεδομένων. εκτέλεσης προκαλώντας απώλεια απόδοσης. Ταυτόχρονα, μπορείτε επίσης να εκχωρήσετε χώρο στο πλαίσιο στοίβας ή να καταχωρήσετε αντίστοιχα, έτσι ώστε το αρχικό αντικείμενο να μην χρειάζεται να εκχωρήσει χώρο ως σύνολο.

περίληψη


Η νέα γενιά είναι η περιοχή όπου τα αντικείμενα γεννιούνται, μεγαλώνουν και πεθαίνουν Ένα αντικείμενο δημιουργείται και χρησιμοποιείται εδώ, και τελικά συλλέγεται από τον συλλέκτη σκουπιδιών και τελειώνει τη ζωή του.

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

Όταν το GC εμφανίζεται στην παλιά γενιά, ονομάζεται MajorGc ή FullGC. Γενικά, η συχνότητα εμφάνισης του MinorGc είναι πολύ υψηλότερη από αυτή του MajorGC, δηλαδή, η συχνότητα συλλογής σκουπιδιών στην παλιά γενιά θα είναι πολύ χαμηλότερη από αυτή στη νέα γενιά.

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

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

αναφέρομαι σε:

https://zhuanlan.zhihu.com/p/693382698

Ιστολόγιο JVM-Heap-Escape Analysis-08-CSDN

Η ανάλυση διαφυγής μνήμης JIT_java απενεργοποιεί την αντικατάσταση βαθμίδας-CSDN Blog

Δείτε όλες τις διαμορφωμένες τιμές παραμέτρων του JVM

java -XX:+PrintFlagsFinal  #输出打印所有参数jvm参数

Εισαγάγετε την περιγραφή της εικόνας εδώ
Εισαγάγετε την περιγραφή της εικόνας εδώ
Εισαγάγετε την περιγραφή της εικόνας εδώ
Εισαγάγετε την περιγραφή της εικόνας εδώ