2012-04-16 7 views
22

Attualmente sto eseguendo il debug di un programma che ha due thread per un processo esterno e questi due thread continuano a leggere Process.getErrorStream() e Process.getInputStream() utilizzando un ciclo while ((i = in.read(buf, 0, buf.length)) >= 0).Utilizzo CPU al 100% infinito su java.io.FileInputStream.readBytes (metodo nativo)

A volte quando il processo esterno si arresta in modo anomalo a causa di un arresto anomalo di JVM (vedere these hs_err_pid.log files), quei thread che leggono lo stdout/stderr di quel processo esterno iniziano a consumare CPU al 100% e non uscire mai. Il corpo del loop non viene eseguito (ho aggiunto un'istruzione di registrazione lì), quindi il loop infinito sembra essere all'interno del metodo nativo java.io.FileInputStream.readBytes.

L'ho riprodotto sia su Windows 7 a 64 bit (jdk1.6.0_30 a 64 bit, jdk1.7.0_03 a 64 bit) e Linux 2.6.18 (jdk1.6.0_21 a 32 bit). Il codice in questione è here ed è utilizzato like this. Vedere quei collegamenti per il codice completo - qui ci sono i pezzi interessanti:

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) { 
    ... 
} 

Le tracce dello stack assomigliano

"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 

o

"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 

Con l'Explorer Sysinternals Process ero in grado di ottenere tracce dello stack nativo di quei thread. Il più delle volte, oltre l'80% del tempo, l'analisi dello stack è simile al seguente:

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 

Questo accade anche abbastanza spesso:

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 

e qualche volta è l'esecuzione di questa parte del codice:

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 

Qualche idea su come risolvere questo problema? Si tratta di un problema noto con JVM? C'è una soluzione?

+3

Potrebbe includere il codice di loop? BTW la condizione '> = 0' è eccessivamente ampia, se' buf.length' non è zero, quindi read() è garantito per leggere almeno 1 byte o return -1 (o lanciare un'eccezione). –

+0

Cosa? 'Process.getInputStream()' restituisce un FileInputStream? –

+0

Sì. In java.lang.ProcessImpl # ProcessImpl è possibile vedere stdout_stream e stderr_stream inizializzati con un FileInputStream. Ha un senso dal punto di vista Unix, dove tutto è un file. –

risposta

1

non ho ancora stato in grado di riprodurre questo a livello locale, ma le due possibili soluzioni potrebbe essere

  • giocare con in.available().

  • reindirizzamento robusta e stderr nel processo esterno ad una presa e leggere questo dal processo di controllo invece.

+0

[Una soluzione rapida che utilizza in.available()] (http://code.google.com/p/pitestrunner/source/browse/pitest/src/main/java/org/pitest/util/StreamMonitor.java?spec = svn0607ac947dd76768f5e852350386bc9c324a6005 & r = 0607ac947dd76768f5e852350386bc9c324a6005 # 59) ha aiutato ad evitare il problema per ora. Stiamo ancora cercando una soluzione migliore e il motivo per cui questo sta accadendo in primo luogo. Cercherò di produrre un [SSCCE] (http://sscce.org/) rimuovendo il codice superfluo dal progetto closed source in cui posso riprodurlo. –

+0

Il problema può essere riprodotto bloccando il processo figlio con il ciclo di allocazione di array infiniti da http://stackoverflow.com/questions/65200/how-do-you-crash-a-jvm e probabilmente le altre tecniche elencate qui . – henry