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.
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]);
}
}
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.
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:
...
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)
É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)
** 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.
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