2012-04-16 5 views
22

Ich bin gerade im Debuggen eines Programms, das zwei Threads pro einem externen Prozess hat, und diese beiden Threads lesen weiter Process.getErrorStream() und Process.getInputStream() mit einer while ((i = in.read(buf, 0, buf.length)) >= 0)-Schleife.Infinite 100% CPU-Auslastung bei java.io.FileInputStream.readBytes (Native Methode)

Manchmal, wenn der externe Prozess aufgrund eines JVM-Absturzes abstürzt (siehe these hs_err_pid.log files), beginnen jene Threads, die den stdout/stderr dieses externen Prozesses lesen, 100% CPU zu verbrauchen und niemals zu beenden. Der Schleifenkörper wird nicht ausgeführt (Ich habe eine Protokollierungsanweisung dort hinzugefügt), so scheint die Endlosschleife innerhalb der nativen Methode java.io.FileInputStream.readBytes zu sein.

Ich habe dies auf Windows 7 64-Bit (jdk1.6.0_30 64-Bit, jdk1.7.0_03 64-Bit) und Linux 2.6.18 (jdk1.6.0_21 32-Bit) reproduziert. Der betreffende Code ist here und wird like this verwendet. Sehen Sie diese Links für den vollständigen Code - hier die interessanten Bits sind:

private final byte[]    buf = new byte[256]; 
private final InputStream   in; 
...  

int i; 
while ((i = this.in.read(this.buf, 0, this.buf.length)) >= 0) { 
    ... 
} 

Die Stack-Traces aussehen

"PIT Stream Monitor" daemon prio=6 tid=0x0000000008869800 nid=0x1f70 runnable [0x000000000d7ff000] 
    java.lang.Thread.State: RUNNABLE 
    at java.io.FileInputStream.readBytes(Native Method) 
    at java.io.FileInputStream.read(FileInputStream.java:220) 
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:218) 
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:258) 
    at java.io.BufferedInputStream.read(BufferedInputStream.java:317) 
    - locked <0x00000007c89d6d90> (a java.io.BufferedInputStream) 
    at org.pitest.util.StreamMonitor.readFromStream(StreamMonitor.java:38) 
    at org.pitest.util.StreamMonitor.process(StreamMonitor.java:32) 
    at org.pitest.util.AbstractMonitor.run(AbstractMonitor.java:19) 
    Locked ownable synchronizers: 
    - None 

oder

"PIT Stream Monitor" daemon prio=6 tid=0x0000000008873000 nid=0x1cb8 runnable [0x000000000e3ff000] 
    java.lang.Thread.State: RUNNABLE 
    at java.io.FileInputStream.readBytes(Native Method) 
    at java.io.FileInputStream.read(FileInputStream.java:220) 
    at org.pitest.util.StreamMonitor.readFromStream(StreamMonitor.java:38) 
    at org.pitest.util.StreamMonitor.process(StreamMonitor.java:32) 
    at org.pitest.util.AbstractMonitor.run(AbstractMonitor.java:19) 
    Locked ownable synchronizers: 
    - None 

Mit dem Sysinternals Process Explorer konnte ich native Stack-Spuren dieser Threads erhalten. Meistens über 80% der Zeit, sieht der Stack-Trace wie folgt aus:

ntdll.dll!NtReadFile+0xa 
KERNELBASE.dll!ReadFile+0x7a 
kernel32.dll!ReadFile+0x59 
java.dll!handleRead+0x2c 
java.dll!VerifyClassCodesForMajorVersion+0x1d1 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

Dies geschieht auch oft:

ntdll.dll!RtlNtStatusToDosErrorNoTeb+0x52 
ntdll.dll!RtlNtStatusToDosError+0x23 
KERNELBASE.dll!GetCurrentThreadId+0x2c 
KERNELBASE.dll!CreatePipe+0x21a 
kernel32.dll!ReadFile+0x59 
java.dll!handleRead+0x2c 
java.dll!VerifyClassCodesForMajorVersion+0x1d1 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

ntdll.dll!RtlNtStatusToDosErrorNoTeb+0x42 
ntdll.dll!RtlNtStatusToDosError+0x23 
KERNELBASE.dll!GetCurrentThreadId+0x2c 
KERNELBASE.dll!CreatePipe+0x21a 
kernel32.dll!ReadFile+0x59 
java.dll!handleRead+0x2c 
java.dll!VerifyClassCodesForMajorVersion+0x1d1 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

und manchmal ist es, diesen Teil des Codes ausführen:

java.dll!VerifyClassCodesForMajorVersion+0xc3 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

java.dll!Java_sun_io_Win32ErrorMode_setErrorMode+0x847c 
java.dll!VerifyClassCodesForMajorVersion+0xd7 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

jvm.dll!JNI_GetCreatedJavaVMs+0x1829f 
java.dll!VerifyClassCodesForMajorVersion+0x128 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

jvm.dll+0x88c1 
jvm.dll!JNI_GetCreatedJavaVMs+0x182a7 
java.dll!VerifyClassCodesForMajorVersion+0x128 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

java.dll!VerifyClassCodesForMajorVersion+0x10b 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

jvm.dll!JNI_CreateJavaVM+0x1423 
java.dll!VerifyClassCodesForMajorVersion+0x190 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

jvm.dll+0x88bf 
jvm.dll!JNI_CreateJavaVM+0x147d 
java.dll!VerifyClassCodesForMajorVersion+0x190 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

java.dll!VerifyClassCodesForMajorVersion+0x1aa 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

java.dll!VerifyClassCodesForMajorVersion+0x1c3 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

java.dll!VerifyClassCodesForMajorVersion+0x224 
java.dll!Java_java_io_FileInputStream_readBytes+0x1d 

Irgendwelche Ideen, wie man dieses Problem löst? Ist das ein bekanntes Problem mit der JVM? Gibt es eine Problemumgehung?

+3

Könnten Sie Ihren Loop-Code einschließen? Übrigens ist die Bedingung '> = 0' zu breit, wenn 'buf.length' nicht Null ist, dann wird read() garantiert mindestens 1 Byte lesen oder -1 zurückgeben (oder eine Exception werfen). –

+0

Was? Gibt 'Process.getInputStream()' einen FileInputStream zurück? –

+0

Ja. In java.lang.ProcessImpl # ProcessImpl können Sie stdout_stream und stderr_stream mit einem FileInputStream initialisieren. Es macht irgendwie Sinn aus der Sicht von Unix, wo alles eine Datei ist. –

Antwort

1

Ich habe noch nicht gelungen, diese lokal zu reproduzieren, aber die zwei mögliche Abhilfen könnte sein

  • mit in.available() Spielen Sie herum.

  • Redirect Stout und Stderr in dem externen Prozess zu einem Sockel und lesen diese aus dem Steuerungsprozess statt.

+0

[Eine schnelle Lösung mit in.available()] (http://code.google.com/p/pitestrunner/source/browse/prest/src/main/java/org/pitest/util/StreamMonitor.java?spec = svn0607ac947dd76768f5e852350386bc9c324a6005 & r = 0607ac947dd76768f5e852350386bc9c324a6005 # 59) hat geholfen, das Problem für jetzt zu vermeiden. Wir suchen immer noch nach einer besseren Lösung und dem Grund, warum dies überhaupt geschieht. Ich werde versuchen, ein [SSCCE] (http://sscce.org/) zu erzeugen, indem ich überflüssigen Code aus dem Closed-Source-Projekt entferne, wo ich das reproduzieren kann. –

+0

Das Problem kann reproduziert werden, indem der untergeordnete Prozess mit der unendlichen Array-Zuordnungsschleife aus http://stackoverflow.com/questions/65200/how-do-you-crash-a-jvm und wahrscheinlich den anderen dort aufgelisteten Techniken abstürzt . – henry