Java verfügt über einen Mechanismus namens Intrinsic Locks. Jedes Objekt verfügt über eine eigene Sperre (Monitor-Sperre), und eine einfache Parallelverarbeitung mit mehreren Threads kann nur mit dem Schlüsselwort "synchronisiert" realisiert werden. Wie arbeiten die Threads zusammen, wenn das Programm ausgeführt wird? Verwenden Sie diesmal bpftrace, um dies zu überprüfen.
OpenJDK 14
Die [Dtrace] -Funktion des JDK (https://docs.oracle.com/javase/8/docs/technotes/guides/vm/dtrace.html) ist erforderlich. Erstellen Sie sie daher aus dem Quellcode.
$ 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
$ 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
Bereiten Sie jetzt dieses Java-Programm vor. Sie rufen lediglich zwei konkurrierende Bedrohungen auf.
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();
}
}
Kompilieren
$ $JAVA_HOME/bin/javac Test.java
Starten Sie zuerst diesen Befehl bpftrace in einem anderen Terminal, um die Ablaufverfolgung vorzubereiten.
$ 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...
Führen Sie als Nächstes das vorherige Java-Programm aus
$ $JAVA_HOME/bin/java -XX:+ExtendedDTraceProbes Test
enter bar
exit bar
enter foo
exit foo
Die beiden Bedrohungen liefen nacheinander. Was ist also mit der Rückverfolgung?
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
Was Sie hier sehen können, ist, dass nach dem Start einer Reihe von JVM-Systembedrohungen auch die Bedrohungen "foo" und "bar" gestartet wurden. Und foo
befindet sich jetzt im Zustand monitor_contended_enter
, da natürlich der bar
zuerst in den kritischen Bereich eingetreten ist. Danach wird "bar" zu "monitor_contended_exit" und verlässt den kritischen Bereich. Sofort wurde "foo" auch zu "monitor_contended_entered" und die Ausführung begann.
Es ist eine einfache Anwendung, aber bpftrace oder bcc ist ein sehr leistungsfähiges Werkzeug, und ich möchte in Zukunft weitere Möglichkeiten ausloten.
Recommended Posts