noyau java: compilateur HotSpot et tas C

Aperçu

Pour la génération de code natif et le traitement d'optimisation effectué par le compilateur HotSpot, une analyse syntaxique du code cible est requise. Le tas natif (tas C) est utilisé comme zone de mémoire utilisée pour l'analyse syntaxique, et lorsqu'une méthode volumineuse ou une syntaxe compliquée est analysée, le tas C est également utilisé en conséquence. Cet article explique comment le compilateur HotSpot augmente le tas C.

En passant, les programmes Java sont optimisés comme n'importe quel autre langage, mais pas lorsqu'ils sont compilés avec javac. Lorsqu'il est optimisé, le programme Java est exécuté, et lorsque le code est la cible de compilation par le compilateur HotSpot, il est optimisé par le compilateur HotSpot.

Faisons un programme simple.

Pour vérifier l'utilisation de la mémoire, laissez l'analyse de syntaxe être effectuée jusqu'à ce que le compilateur abandonne, puis vérifiez le changement dans l'utilisation de la mémoire avant et après cela. Le compilateur HotSpot n'a pas pu trouver un bon moyen d'abandonner Out Of Nodes, ce qui entraîne un code infiniment long.

Pour le programme entier, voir https://github.com/takimai39/sample/blob/master/OutOfNodes.java.

OutOfNodes.java


import java.io.*;
 
public class OutOfNodes {
    public static void main(String[] args) {
        for ( int n = 0 ; n < 2 ; n++ ) {
        // CompileThreshold=Puisqu'il est 10 000, le compilateur HotSpot n'est pas utilisé pour la première boucle.
            System.out.println("Hit return to continue"); keyin();
            for ( int x = 0 ; x < 9999; x++) {
                challenge(); 
            }
        }
        System.out.println("Hit return to exit"); keyin();
    }

    private static void keyin() {
        BufferedReader userInputStreamReader = new BufferedReader(new InputStreamReader(System.in));
        try {
            String inputString = userInputStreamReader.readLine();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getX() {
        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        return(str);
    }

    private static String challenge() {

        String a[] = new String[300];
        String b[] = new String[300];

        a[0] = getX();
        a[1] = getX();
        // ...Omis, répétez ce modèle à l'infini 230 fois... 
        b[0] = 0 + a[0];
        b[1] = 1 + a[1];
        // ...Répétez 230 fois de la même manière...
        return(b[0]);
    }
}

Exécutez le programme et vérifiez l'utilisation de la mémoire

Essayez de l'exécuter avec l'option PrintCompilation pour voir quelles méthodes le compilateur HotSpot compile. Le format d'affichage est à gauche Horodatage compilation_number Caractère d'attribut Nom de classe :: Nom de la méthode (taille du code)

$ java -XX:+PrintCompilation OutOfNodes
Hit return to continue
   3106    1     n       java.lang.System::arraycopy (native)   (static)
   3108    2             java.lang.Integer::stringSize (21 bytes)
   3110    3             java.lang.Integer::getChars (131 bytes)
   3118    4             java.lang.Object::<init> (1 bytes)
   3118    5             java.lang.Math::min (11 bytes)
   3118    6             java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes)
   3121    7             java.lang.String::length (6 bytes)
   3121    9             java.lang.AbstractStringBuilder::append (48 bytes)
   3124   10             java.lang.String::<init> (67 bytes)
   3125   11             java.util.Arrays::copyOfRange (63 bytes)
   3127   12             java.lang.StringBuilder::append (8 bytes)
   3129    8             java.lang.String::getChars (62 bytes)
   3130   13             java.lang.AbstractStringBuilder::<init> (12 bytes)
   3130   14             java.lang.StringBuilder::<init> (7 bytes)
   3130   15             java.lang.StringBuilder::toString (17 bytes)
   3130   16             OutOfNodes::getX (5 bytes)
   3130   17             java.lang.StringBuilder::append (8 bytes)
   3132   18             java.lang.AbstractStringBuilder::append (62 bytes)
Hit return to continue
 841500   19             OutOfNodes::challenge (7992 bytes)
 841658   20 %           OutOfNodes::main @ 20 (55 bytes)
Hit return to exit
 846756   19             OutOfNodes::challenge (7992 bytes)   COMPILE SKIPPED: out of nodes during split (not retryable)


Sur un autre écran de terminal, essayez d'obtenir le fichier principal tout en observant la taille du processus. Après avoir confirmé COMPILE SKIPPED:, lorsque vous vérifiez à nouveau la taille, elle augmente d'environ 64 Mo.

$ pmap `jps | grep OutOfNodes | awk '{print $1}' ` | grep total
 total          2127304K

$ pmap `jps | grep OutOfNodes | awk '{print $1}' ` | grep total
 total          2192840K

Pour une confirmation ultérieure, récupérez le fichier core pour chaque situation avec la commande gcore.

Vérifiez le fichier core avec gdb plus (CORE ANALYZER)

Vous pouvez générer des commandes telles que pmap, mais il existe CORE ANALYZER comme outil pour vérifier la zone mémoire allouée par malloc () etc. C'est un excellent outil, très apprécié par quiconque vérifie le fichier de base. Les commandes telles que heap / v peuvent être utilisées avec gdb plus inclus dans cet analyseur principal. De plus, vous pouvez vérifier la répartition de chaque arène avec heap / c, vous pouvez donc l'utiliser pour vérifier combien d'octets de zone sont mallocés à partir du résultat de sortie.


$ /work/core_analyzer-master/bin/Linux/ptmalloc/gdb-7.11.1/gdb /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.141-2.6.10.5.el7.x86_64/bin/java ./core.24437.before

(gdb) heap /v Avant l'augmentation de l'utilisation de la mémoire
Warning: 17 mmap memory blocks were found while mp_ reports 16
Python Exception <type 'exceptions.NameError'> Installation error: gdb.execute_unwinders function is missing: 
        Tuning params & stats:
                mmap_threshold=131072
                pagesize=4096
                n_mmaps=16
                n_mmaps_max=65536
                total mmap regions created=16
                mmapped_mem=11202560
                sbrk_base=0x15b8000
        Main arena (0x7fb6ab64a760) owns regions:
                [0x15d900f - 0x15d9000] Total 17179869184.0GBFailed to get the first chunk at 0x15d8fff

        Dynamic arena (0x7fb670000020) owns regions:
                [0x7fb6700008c0 - 0x7fb670021000] Total 129KB in-use 1(312) free 3(129KB)
        Dynamic arena (0x7fb66c000020) owns regions:
                [0x7fb66c0008c0 - 0x7fb66c021000] Total 129KB in-use 1(312) free 2(129KB)
        Dynamic arena (0x7fb678000020) owns regions:
                [0x7fb6780008c0 - 0x7fb678021000] Total 129KB in-use 1(312) free 2(129KB)
        Dynamic arena (0x7fb674000020) owns regions:
                [0x7fb6740008c0 - 0x7fb674021000] Total 129KB in-use 11(3KB) free 1(126KB)
        Dynamic arena (0x7fb67c000020) owns regions:
                [0x7fb67c0008c0 - 0x7fb67c021000] Total 129KB in-use 1(312) free 2(129KB)
        Dynamic arena (0x7fb68c000020) owns regions:
                [0x7fb68c0008c0 - 0x7fb68c021000] Total 129KB in-use 10(1KB) free 3(128KB)
        Dynamic arena (0x7fb688000020) owns regions:
                [0x7fb6880008c0 - 0x7fb688021000] Total 129KB in-use 3(22KB) free 2(107KB)
        Dynamic arena (0x7fb694000020) owns regions:
                [0x7fb6940008c0 - 0x7fb694021000] Total 129KB in-use 1(312) free 2(129KB)
        Dynamic arena (0x7fb690000020) owns regions:
                [0x7fb6900008c0 - 0x7fb690021000] Total 129KB in-use 0(0) free 2(129KB)
        Dynamic arena (0x7fb698000020) owns regions:
                [0x7fb6980008c0 - 0x7fb698021000] Total 129KB in-use 0(0) free 2(129KB)
        Dynamic arena (0x7fb6a4000020) owns regions:
                [0x7fb6a40008c0 - 0x7fb6a40fe000] Total 1013KB in-use 1622(964KB) free 4(36KB)
        mmap-ed large memory blocks:

... 

Ce qui suit est le compilateur HotSpot COMPILE SKIPPED: out of nodes...C'est l'utilisation de la mémoire après le devenir.

(gdb) heap /v Après une utilisation accrue de la mémoire
Warning: 18 mmap memory blocks were found while mp_ reports 16
Python Exception <type 'exceptions.NameError'> Installation error: gdb.execute_unwinders function is missing: 
        Tuning params & stats:
                mmap_threshold=8527872
                pagesize=4096
                n_mmaps=16
                n_mmaps_max=65536
                total mmap regions created=23
                mmapped_mem=11202560
                sbrk_base=0x15b8000
        Main arena (0x7fb6ab64a760) owns regions:
                [0x15d900f - 0x15d9000] Total 17179869184.0GBFailed to get the first chunk at 0x15d8fff

        Dynamic arena (0x7fb670000020) owns regions:
                [0x7fb6700008c0 - 0x7fb670021000] Total 129KB in-use 1(312) free 3(129KB)
        Dynamic arena (0x7fb66c000020) owns regions:
                [0x7fb66c0008c0 - 0x7fb66c021000] Total 129KB in-use 1(312) free 2(129KB)
        Dynamic arena (0x7fb678000020) owns regions:
                [0x7fb6780008c0 - 0x7fb67807e000] Total 501KB in-use 14(6KB) free 4(494KB)
+       Dynamic arena (0x7fb674000020) owns regions: <--Cette arène est particulièrement en augmentation!
+               [0x7fb664000030 - 0x7fb667b15000] Total 59MB in-use 4(127KB) free 4(58MB)
+               [0x7fb6740008c0 - 0x7fb678000000] Total 63MB in-use 49(51KB) free 12(63MB)
        Dynamic arena (0x7fb67c000020) owns regions:
                [0x7fb67c0008c0 - 0x7fb67c021000] Total 129KB in-use 1(312) free 2(129KB)
        Dynamic arena (0x7fb68c000020) owns regions:
                [0x7fb68c0008c0 - 0x7fb68c021000] Total 129KB in-use 10(1KB) free 3(128KB)
        Dynamic arena (0x7fb688000020) owns regions:
                [0x7fb6880008c0 - 0x7fb688021000] Total 129KB in-use 3(22KB) free 2(107KB)
        Dynamic arena (0x7fb694000020) owns regions:
                [0x7fb6940008c0 - 0x7fb694021000] Total 129KB in-use 4(560) free 1(129KB)
        Dynamic arena (0x7fb690000020) owns regions:
                [0x7fb6900008c0 - 0x7fb690021000] Total 129KB in-use 0(0) free 1(129KB)
        Dynamic arena (0x7fb698000020) owns regions:
                [0x7fb6980008c0 - 0x7fb698021000] Total 129KB in-use 10(6KB) free 2(122KB)
        Dynamic arena (0x7fb6a4000020) owns regions:
                [0x7fb6a40008c0 - 0x7fb6a40fe000] Total 1013KB in-use 1643(839KB) free 7(161KB)
        mmap-ed large memory blocks:

... 

Vérifiez la répartition de l'arène accrue

Vous pouvez voir une autre ventilation de l'arène ci-dessus avec la commande heap / c. La surface augmentée est de 56380088 octets libres, ce qui est presque gratuit et inutilisé. En d'autres termes, la zone qui était autrefois utilisée par malloc () mais qui n'est plus nécessaire et qui est libérée est comptée.

(gdb) heap /c 0x7fb664000030
        Dynamic arena (0x7fb674000020): [0x7fb664000020 - 0x7fb667b15000]
                [0x7fb664000030 - 0x7fb6675c4ae8] 56380088 bytes free
                        [0x7fb6675c4af0 - 0x7fb6675ccad8] 32744 bytes inuse
                [0x7fb6675ccae0 - 0x7fb6677ac8b8] 1965528 bytes free
                        [0x7fb6677ac8c0 - 0x7fb6677b48a8] 32744 bytes inuse
                [0x7fb6677b48b0 - 0x7fb667994868] 1966008 bytes free
                        [0x7fb667994870 - 0x7fb66799c858] 32744 bytes inuse
                        [0x7fb66799c860 - 0x7fb6679a4848] 32744 bytes inuse
                [0x7fb6679a4850 - 0x7fb667b15000] 1509296 bytes free

        Total inuse 4 blocks 130976 bytes
        Total free 4 blocks 61820920 bytes
(gdb)

Utilisons les informations de empty_block comme indice.

Étant donné qu'il n'existe qu'un fichier de base pour l'information, il est très difficile de déterminer à quoi servait auparavant la zone free (). Cependant, si vous pensez toujours à quelque chose, les informations d'adresse de empty_block peuvent être disponibles comme un indice dans ce cas. Le compilateur HotSpot alloue une zone de mémoire à utiliser dans les unités appelées Chunk, et définit l'adresse de empty_block lorsqu'il a fini de l'utiliser.

Extrait de l'index du code source OpenJDK.cpp
...
    184 //---------------------------- IndexSet::free_block() -------------------------
    185 // Add a BitBlock to the free list.
    186 
    187 void IndexSet::free_block(uint i) {
    188   debug_only(check_watch("free block", i));
    189   assert(i < _max_blocks, "block index too large");
    190   BitBlock *block = _blocks[i];
    191   assert(block != &_empty_block, "cannot free the empty block");
    192   block->set_next((IndexSet::BitBlock*)Compile::current()->indexSet_free_block_list());
    193   Compile::current()->set_indexSet_free_block_list(block);
    194   set_block(i,&_empty_block);
    195 }

Vérifiez l'adresse de empty_block avec gdb et voyez combien vous pouvez trouver dans le fichier core. Vous pouvez voir qu'il y a de nombreuses adresses empty_block dans le noyau après qu'il soit devenu COMPILE SKIPPED.

(gdb) info var empty_block
All variables matching regular expression "empty_block":

File /usr/src/debug/java-1.7.0-openjdk-1.7.0.141-2.6.10.5.el7.x86_64/openjdk/hotspot/src/share/vm/opto/indexSet.cpp:
IndexSet::BitBlock IndexSet::_empty_block;

Non-debugging symbols:
0x00007fb6ab053ac0  IndexSet::_empty_block
(gdb) 

$ od -An -x  -w8 core.24437.before | grep "7fb6 0000 3ac0 ab05" | wc -l
1
$ od -An -x  -w8 core.24437.after | grep "7fb6 0000 3ac0 ab05" | wc -l
54655
$

Quand j'essaye de vider la zone autour de cette zone, c'est moche.

$ gdb /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.141-2.6.10.5.el7.x86_64/bin/java ./core.24437.after
...
(gdb) x/20gx 0x7fb665322f30
0x7fb665322f30: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322f40: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322f50: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322f60: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322f70: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322f80: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322f90: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322fa0: 0x00007fb6756461c0      0x00007fb6ab053ac0
0x7fb665322fb0: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322fc0: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
(gdb) 
0x7fb665322fd0: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322fe0: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665322ff0: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665323000: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665323010: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665323020: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665323030: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665323040: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665323050: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
0x7fb665323060: 0x00007fb6ab053ac0      0x00007fb6ab053ac0
(gdb) 

Je veux m'organiser un peu

** Qu'est-ce qu'un nœud? ** ** Le nœud dans le message COMPILE SKIPPED: out of nodes during split ... est une donnée après avoir été décomposée en une arborescence par analyse syntaxique au moment de la compilation.

Avez-vous besoin de prendre des mesures?

Je pense qu'il est rare que l'utilisation de la mémoire de 64 Mo augmente et que le temps de compilation de 5 secondes entraîne des dommages réels. Comme la compilation échoue toujours, spécifiez exclude dans le fichier .hotspot_compiler pour éviter qu'il ne soit ciblé par le compilateur HotSpot. Ou, au contraire, au lieu de l'exclure, il est possible d'augmenter MaxNodeLimit et de le compiler. Si vous pouvez changer le programme Java que vous exécutez, vous pourriez simplifier encore plus le code en question.

Je pense que ce sera une décision après des tests approfondis, mais je pensais personnellement que les paramètres par défaut pourraient être utilisés s'il n'y avait aucun dommage réel.

Recommended Posts

noyau java: compilateur HotSpot et tas C
noyau Java: le compilateur HotSpot plante sur SIGSEGV
Histoire de remplacement C # et Java
Mécanisme de référence Java (pile et tas)
Crypter avec Java et décrypter avec C #
Lier le code Java et C ++ avec SWIG
java core: Vous ne pouvez pas atteindre safepoint et vous bloquer? !!
La direction de Java dans "C ++ Design and Evolution"
De Java à C et de C à Java dans Android Studio
Différences dans l'écriture des classes Java, C # et Javascript
Résumer les différences entre l'écriture C # et Java
Java et JavaScript
XXE et Java
Résolution avec Ruby, Perl et Java AtCoder ABC 128 C
Organiser les builds dans des combinaisons C ++ / Java et Win / Linux
Langage Java du point de vue de Kotlin et C #
Getter et Setter (Java)
[Java] Thread et exécutable
Java vrai et faux
[Java] Comparaison des chaînes de caractères et && et ||
noyau java: fichier de base haché
Brainfuck-> Compilateur de code d'octet Java
Java - Sérialisation et désérialisation
[Java] Arguments et paramètres
timedatectl et Java TimeZone
[Java] Branchement et répétition
[Java] Types de variables et types
java (classe et instance)
[Java] Surcharge et remplacement
Résolution avec Ruby, Perl et Java AtCoder ABC 113 C Reference
Kotlin post- et pré-incrémentation et surcharge des opérateurs (comparaison avec C, Java, C ++)
Résolution avec Ruby, Perl et Java AtCoder ABC 129 C (Partie 1)