Vérifiez le comportement de Java Intrinsic Locks avec bpftrace

Aperçu

Java a un mécanisme appelé Intrinsic Locks. Chaque objet a son propre verrou unique (Monitor Lock), et un traitement parallèle multi-thread facile peut être réalisé avec juste le mot-clé «synchronized». Alors, comment les threads fonctionnent-ils ensemble lorsque le programme est en cours d'exécution? Cette fois, utilisez bpftrace pour vérifier cela.

Environnement de travail

Le système d'exploitation doit être Ubuntu 20.04.

OpenJDK 14

La fonction [Dtrace] du JDK (https://docs.oracle.com/javase/8/docs/technotes/guides/vm/dtrace.html) est requise, donc compilez-la à partir des sources.

$ apt-get update
$ DEBIAN_FRONTEND=noninteractive ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && apt-get install -y tzdata && dpkg-reconfigure --frontend noninteractive tzdata
$ apt-get install build-essential autoconf systemtap-sdt-dev libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev libcups2-dev libfontconfig1-dev libasound2-dev unzip zip ccache -y

## clone source and boot jdk
$ mkdir jdk && cd jdk
$ git clone https://github.com/openjdk/jdk14u.git

$ wget https://download.java.net/java/GA/jdk13.0.2/d4173c853231432d94f001e99d882ca7/8/GPL/openjdk-13.0.2_linux-x64_bin.tar.gz
$ tar xvf openjdk-13.0.2_linux-x64_bin.tar.gz

$ cd jdk14u

$ bash configure --enable-debug --with-jvm-variants=server --enable-dtrace --with-boot-jdk=../jdk-13.0.2 --enable-ccache
$ make images

$ export JAVA_HOME=$PWD/build/linux-x86_64-server-fastdebug/jdk

bcc et bpftrace

## bcc
$ apt-get install -y linux-headers-$(uname -r) bison build-essential cmake flex g++ git libelf-dev zlib1g-dev libfl-dev systemtap-sdt-dev binutils-dev llvm-8-dev llvm-8-runtime libclang-8-dev clang-8 arping netperf iperf3 python3-distutils
$ git clone --recurse-submodules https://github.com/iovisor/bcc.git
$ mkdir bcc/build; cd bcc/build
$ cmake -DPYTHON_CMD=python3 ..
$ make -j8 && make install && ldconfig

$ cd ../..

## bpftrace

$ git clone https://github.com/iovisor/bpftrace.git
$ mkdir bpftrace/build; cd bpftrace/build

$ cmake -DHAVE_BCC_PROG_LOAD=ON -DHAVE_BCC_CREATE_MAP=ON -DBUILD_TESTING=OFF ..
$ make -j8 && make install

Préparez un exemple de programme

Maintenant, préparez ce programme java. Tout ce que vous faites, c'est appeler deux menaces concurrentes.

Test.java


public class Test {

    private static synchronized void enter(String name) {
        try {
            System.out.println("enter " + name);
            Thread.sleep(1000);
            System.out.println("exit " + name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Thread(() -> enter("foo"), "foo").start();
        new Thread(() -> enter("bar"), "bar").start();
    }
}

Compiler

$ $JAVA_HOME/bin/javac Test.java

Exécution et résultats

Commencez par lancer cette commande bpftrace dans un autre terminal pour préparer la trace.

$ bpftrace - <<EOF
BEGIN { printf("%-12s %-6s %-27s %s\n", "TIME", "TID", "ACTION", "DESC"); }

usdt:$JAVA_HOME/lib/server/libjvm.so:hotspot:thread__start { printf("%-12ld %-6d %-27s", elapsed, arg2, "start");printf("name=%s\n", str(arg0)); }
usdt:$JAVA_HOME/lib/server/libjvm.so:hotspot:thread__stop { printf("%-12ld %-6d %-27s", elapsed, arg2, "stop");printf("name=%s\n", str(arg0)); }

usdt:$JAVA_HOME/lib/server/libjvm.so:hotspot:monitor__contended__enter { printf("%-12ld %-6d %-27s", elapsed, arg0, "monitor_contended_enter"); printf("monitor=0x%lx, class=%s\n", arg1, str(arg2)); }
usdt:$JAVA_HOME/lib/server/libjvm.so:hotspot:monitor__contended__entered { printf("%-12ld %-6d %-27s", elapsed, arg0, "monitor_contended_entered"); printf("monitor=0x%lx, class=%s\n", arg1, str(arg2)); }
usdt:$JAVA_HOME/lib/server/libjvm.so:hotspot:monitor__contended__exit { printf("%-12ld %-6d %-27s", elapsed, arg0, "monitor_contended_exit"); printf("monitor=0x%lx, class=%s\n", arg1, str(arg2)); }
EOF

Attaching 6 probes...

Ensuite, exécutez le programme java précédent

$ $JAVA_HOME/bin/java -XX:+ExtendedDTraceProbes  Test
enter bar
exit bar
enter foo
exit foo

Les deux menaces se sont succédées. Alors qu'en est-il du traçage?

Attaching 6 probes...
TIME         TID    ACTION                      DESC
3568183750   2      start                      name=Reference Handler
3568644123   3      start                      name=Finalizer
3581708591   1      monitor_contended_exit     monitor=0x7f7314003100, class=java/lang/Object����
3583984755   4      start                      name=Signal Dispatcher
3584404128   6      start                      name=C2 CompilerThread0
3584475441   5      start                      name=Service Thread
3584812927   7      start                      name=C1 CompilerThread0
3585382491   8      start                      name=Sweeper thread
3618900047   9      start                      name=Common-Cleaner
4170272484   2      monitor_contended_exit     monitor=0x7f7314005100, class=java/lang/ref/ReferenceQueue$Lock���#
4176664886   10     start                      name=Notification Thread
4201863620   11     start                      name=foo
4202328663   12     start                      name=bar
4203255448   11     monitor_contended_enter    monitor=0x7f7314007000, class=java/lang/Class�����
5229989467   12     monitor_contended_exit     monitor=0x7f7314007000, class=java/lang/Class�����
5230226530   11     monitor_contended_entered  monitor=0x7f7314007000, class=java/lang/Class�����
5230593438   12     stop                       name=bar
6242293321   11     stop                       name=foo
6247012424   4      stop                       name=Signal Dispatcher

Ce que vous pouvez voir ici, c'est qu'après le lancement d'un certain nombre de menaces système JVM, les menaces foo et bar ont été lancées. Et foo est maintenant dans l'état de monitor_contended_enter, parce que bien sûr la barre est entrée en premier dans la région critique. Après cela, bar devient monitor_contended_exit et quitte la région critique. Immédiatement, foo est également devenu monitor_contended_entered et l'exécution a commencé.

Résumé

C'est une application simple, mais bpftrace ou bcc est un outil très puissant, et j'aimerais explorer plus de possibilités à l'avenir.

Matériel de référence

Recommended Posts

Vérifiez le comportement de Java Intrinsic Locks avec bpftrace
Vérifiez le contenu du magasin de certificats Java
Vérifiez le contenu des paramètres avec le levier
Mémo: [Java] Vérifiez le contenu du répertoire
[Java] Vérifiez le nombre d'occurrences de caractères
[Java] [Spring] Tester le comportement de l'enregistreur
Vérifiez l'enregistrement MX de l'adresse e-mail avec java et vérifiez le domaine
Calculer le score de similarité des chaînes de caractères avec JAVA
Résumé du comportement de ToString avec les annotations Java et Groovy
[Java] Vérifiez la version JDK du fichier war construit
Surveillez l'état interne des programmes Java avec Kubernetes
Vérifiez le résultat de l'inférence de paramètre de type générique avec JShell
Vérifiez le comportement de include, exclude et ExhaustedRetryException de Spring Retry
L'histoire de la création de DTO, semblable à Dao avec Java, SQLite
Remplacez seulement une partie de l'hôte URL par java
Je n'ai pas vraiment compris le comportement de Java Scanner et .nextLine ()
Commande pour vérifier le nombre et l'état des threads Java
Vérifier le fonctionnement de deux rôles avec une application de chat
Vérifiez l'état des applications Java sans utiliser d'outils de surveillance
Les débutants en Java ont brièvement résumé le comportement de Array et ArrayList
[Java] Simplifiez la mise en œuvre de la gestion de l'historique des données avec Reladomo
[Java] Vérifiez la différence entre orElse et orElseGet avec IntStream
À propos du comportement lors de la création d'un mappage de fichiers avec Java
Assurez-vous de comparer le résultat Java compareTo avec 0
À propos du comportement de ruby Hash # ==
[Java 8] Suppression en double (et vérification en double) avec Stream
[Java] Supprimer les éléments de la liste
[Rails] Vérifiez le contenu de l'objet
[Java1.8 +] Obtenez la date du jour × suivant avec LocalDate
[Édition Java] Histoire de la sérialisation
Vérifiez la version de Cent OS
L'histoire de ne pas connaître le comportement de String en passant par Java
Essayez HelloWorld avec la configuration minimale de Heroku Java spring-boot
[Java] Vérification de l'existence des éléments avec Stream
Vérifier l'état de migration des rails
Suivez le lien avec Selenium (Java)
Une histoire sur l'utilisation de l'API League Of Legends avec JAVA
J'ai essayé de vérifier le fonctionnement du serveur gRPC avec grpcurl
Le point addictif lors de l'authentification de base avec Java URLConnection
Voir le comportement des mises à jour d'entités avec Spring Boot + Spring Data JPA
Écraser le téléchargement du fichier avec le même nom avec BOX SDK (java)
Comment vérifier le contenu de la chaîne de caractères java de longueur fixe
Représentez graphiquement les informations du capteur de Raspberry Pi en Java et vérifiez-les avec un navigateur Web
Le comportement de JS fonctionnant sur `Java SE 8 Nashorn` a soudainement changé
La version d'Elasticsearch que vous utilisez est-elle compatible avec Java 11?
L'origine des expressions Java lambda
L'histoire de la création d'un lanceur de jeu avec une fonction de chargement automatique [Java]
Exprimons le résultat de l'analyse du code d'octet Java dans un diagramme de classes
[Java] Vérifiez si la chaîne de caractères est composée uniquement de blancs (= Vierge)
[Java] Récupère MimeType à partir du contenu du fichier avec Apathce Tika [Kotlin]
Vérifiez le fonctionnement à l'aide de la jetée avec Maven.
Obtenez le résultat de POST en Java
Examiner l'utilisation de la mémoire des éléments Java
[Java] Obtenez le jour d'un jour spécifique
Comparer les éléments d'un tableau (Java)
[jour: 5] J'ai résumé les bases de Java
Quelles sont les fonctionnalités mises à jour de Java 13
Mesurez facilement la taille des objets Java
Retour sur les bases de Java
Vérifiez le contenu du traitement avec [rails] binding.pry
Sortie du livre "Introduction à Java"