Fazit vom 21.03.2018:
Experimentiert mit zwei CentOS6 / 7-Typen (beide x86_64-Version). Beginnen Sie mit der Compute Engine von GCP und installieren Sie die folgenden Pakete.
sudo yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
sudo yum groupinstall -y "Development tools"
Details zur Java-Version (bei der Überprüfung des 21.03.2018 wurde diese wie in der CentOS 6/7-Umgebung angezeigt)
$ java -version
openjdk version "1.8.0_161"
OpenJDK Runtime Environment (build 1.8.0_161-b14)
OpenJDK 64-Bit Server VM (build 25.161-b14, mixed mode)
$ javac -version
javac 1.8.0_161
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class InfiniteLoop {
static class Looper implements Runnable {
@Override
public void run() {
int cnt = 0;
while (true) {
//Endlosschleife, die Interrupts ignoriert
try {
System.out.println("InifiniteLoop-Looper count " + cnt);
Thread.sleep(2000);
} catch (InterruptedException ignored) {
}
cnt++;
}
}
}
public static void main(String[] args) {
ExecutorService pool = Executors.newSingleThreadExecutor();
Future<?> f = pool.submit(new Looper());
try {
// Future.get()Warten, bis die Aufgabe beendet ist
f.get();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
kompilieren:
$ javac InfiniteLoop.java
Referenz:
Führen Sie die InfiniteLoop-Klasse mit der JVM-Option aus, um sie später mit jdb zu verbinden.
$ java -Xrunjdwp:transport=dt_socket,address=9000,server=y,suspend=n InfiniteLoop
Holen Sie sich die Java-PID von einem anderen Terminal und überprüfen Sie den Java-Stack-Trace mit jstack.
$ pidof java
13667
$ jstack 13667
2018-03-21 07:52:59
Full thread dump OpenJDK 64-Bit Server VM (25.161-b14 mixed mode):
(...)
"pool-1-thread-1" #10 prio=5 os_prio=0 tid=0x00007f0d840f3b30 nid=0x356f waiting on condition [0x00007f0d6d8c8000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at InfiniteLoop$Looper.run(InfiniteLoop.java:14)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
(...)
Aus dem Stack-Trace konnten wir bestätigen, dass sich der Thread mit dem Namen "pool-1-thread-1" in einer Endlosschleife befand.
Nachdem wir den Threadnamen kennen, starten wir jdb, hängen ihn an die JVM an und beenden den Thread in der Schleife.
$ jdb -attach 9000
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
(Threadliste anzeigen)
> threads
Group system:
(java.lang.ref.Reference$ReferenceHandler)0x1b9 Reference Handler cond. waiting
(java.lang.ref.Finalizer$FinalizerThread)0x1ba Finalizer cond. waiting
(java.lang.Thread)0x1bb Signal Dispatcher running
Group main:
(java.lang.Thread)0x1 main cond. waiting
(java.lang.Thread)0x1bd pool-1-thread-1 sleeping
(Threadname"pool-1-thread-1",Stellen Sie sicher, dass 0x1bd der Thread in der Schleife ist.)
(Thread, den Sie stoppen möchten:Unterbrechen Sie 0x1bd und töten Sie, während Sie Exception nach dem Treten auslösen)
> thread 0x1bd
pool-1-thread-1[1] suspend 0x1bd
pool-1-thread-1[1] step
>
Step completed: "thread=pool-1-thread-1", InfiniteLoop$Looper.run(), line=16 bci=33
16 }
pool-1-thread-1[1] kill 0x1bd new java.lang.Exception("kill from jdb")
killing thread: pool-1-thread-1
pool-1-thread-1[1] instance of java.lang.Thread(name='pool-1-thread-1', id=445) killed
(Beenden Sie den Debugger nach dem Neustart mit cont)
pool-1-thread-1[1] cont
> exit
Das Ausführungsergebnis von Infinite Loop wurde durch den Betrieb von jdb wie folgt beeinflusst.
$ java -Xrunjdwp:transport=dt_socket,address=9000,server=y,suspend=n InfiniteLoop
(...)
InifiniteLoop-Looper count 32
InifiniteLoop-Looper count 33
InifiniteLoop-Looper count 34
(Führen Sie suspend von jdb aus->Zählanzeige stoppt)
(Beim Töten von jdb wird Folgendes angezeigt)
java.util.concurrent.ExecutionException: java.lang.Exception: kill from jdb
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at InfiniteLoop.main(InfiniteLoop.java:27)
Caused by: java.lang.Exception: kill from jdb
at InfiniteLoop$Looper.run(InfiniteLoop.java:16)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
(Wenn Sie jdb beenden, wird Folgendes angezeigt)
Listening for transport dt_socket at address: 9000
(Ctrl-Beenden Sie mit C.)
Es ist ersichtlich, dass die ausführbare Aufgabe durch Beenden von jdb beendet wird und die Wartezeit von Future.get () durch Auslösen einer ExecutionException freigegeben wird. Sie können auch sehen, dass die Ursache für die ExecutionException die Ausnahme ist, die durch das Beenden von jdb ausgelöst wird.
Referenz:
Führen Sie die InfiniteLoop-Klasse ohne spezielle Debugging-Optionen aus.
$ java InfiniteLoop
Überprüfen Sie den Speicherort der ausführbaren Java-Datei:
$ which java
/usr/bin/java
$ ls -l /usr/bin/java
(...) /usr/bin/java -> /etc/alternatives/java
$ ls -l /etc/alternatives/java
(...) /etc/alternatives/java -> /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java
$ ls -l /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java
(...) /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java
Überprüfen Sie die Java-PID und erhalten Sie eine Stapelverfolgung.
$ jstack `pidof java`
2018-03-21 08:28:38
Full thread dump OpenJDK 64-Bit Server VM (25.161-b14 mixed mode):
(...)
"pool-1-thread-1" #8 prio=5 os_prio=0 tid=0x00007ffb500ed000 nid=0x6a05 waiting on condition [0x00007ffb54146000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at InfiniteLoop$Looper.run(InfiniteLoop.java:14)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
(...)
Unter Linux ist nid = 0x ...
der von jstack angezeigten Thread-Informationen die von pthread_kill (2) angegebene LWP-ID.
Fügen Sie mit dem ausführbaren Dateinamen "gdb" PID "hinzu und führen Sie den Befehl" info threads "aus.
$ gdb /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java `pidof java`
(gdb) info threads
(...)
4 Thread 0x7ffb54248700 (LWP 27140) 0x0000003e2020ba5e in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
3 Thread 0x7ffb54147700 (LWP 27141) 0x0000003e2020ba5e in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
2 Thread 0x7ffb40fff700 (LWP 27164) 0x0000003e2020eb2d in accept () from /lib64/libpthread.so.0
* 1 Thread 0x7ffb57342700 (LWP 27131) 0x0000003e202082fd in pthread_join () from /lib64/libpthread.so.0
Das Ergebnis der Ausführung des Befehls "info threads" mit gdb ist die Dezimalanzeige.
Wenn Sie nach 27141
suchen, dem Endlosschleifen-Thread, der dieses Mal angezeigt wird, wobei nid = 0x6a05
in eine Dezimalzahl konvertiert wurde, entspricht der dritte Thread von unten diesem.
3 Thread 0x7ffb54147700 (LWP 27141) 0x0000003e2020ba5e in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
Also nennen wir hier call pthread_kill (pthread_t, int)
... ** Was soll ich für das erste Argument angeben? ** Welche der entsprechenden Zeilen des Ausführungsergebnisses "Info-Threads" sollte angegeben werden?
** Was sollte auch angegeben werden, damit das Signal des zweiten Arguments Javas Thread.stop ()
entspricht? ** ** **
** Wie kann ich zunächst genau das gleiche Verhalten aufrufen, wie wenn ein Thread in Java innerhalb des von gdb angehängten Prozesses beendet wird? ** ** **
→ Dies ist eine völlige Pattsituation. Selbst wenn ich versuchte, es richtig aufzurufen, endete der Java-Prozess mit SIGSEGV, und obwohl es nicht so viel war, führte es nicht dazu, dass "nur der außer Kontrolle geratene Thread isoliert und beendet wurde".
Selbst wenn gdb den Prozess des Auslösens einer Ausnahme von innen reproduzieren kann, wie beim Beenden mit jdb durch Lesen des Java-Quellcodes oder Parsen des Systemaufrufs mit strace, handelt es sich wahrscheinlich um eine beträchtliche interne Implementierung. Ist es nicht abhängig und nicht stabil und in einer tatsächlichen Betriebsumgebung leicht reproduzierbar?
call pthread_kill ()
oder call raise ()
zu senden, um es zu beenden, aber in diesem Experiment können Sie das richtige Verfahren herausfinden. Ich konnte es nicht verifizieren.
――Selbst wenn dies möglich ist, unterscheidet sich die Situation von der Thread-Beendigung in der ursprünglichen JVM, sodass es sehr wahrscheinlich ist, dass die JVM selbst abnormal beendet wird.Es war nicht ordentlich organisiert, aber das war's.
Recommended Posts