2012-04-16 13 views
22

Mam teraz debugowanie programu, który ma dwa wątki na jeden proces zewnętrzny, a te dwa wątki wciąż czytają Process.getErrorStream() i Process.getInputStream() przy użyciu pętli while ((i = in.read(buf, 0, buf.length)) >= 0).Nieskończone 100% użycie procesora w java.io.FileInputStream.readBytes (metoda macierzysta)

Czasami, gdy proces zewnętrzny ulega awarii z powodu awarii maszyny JVM (patrz these hs_err_pid.log files), wątki odczytujące stdout/stderr z tego zewnętrznego procesu zaczynają zużywać 100% procesora i nigdy nie kończą się. Korpus pętli nie jest wykonywany (Dodałem tam zapis logujący), więc nieskończona pętla wydaje się znajdować w natywnej metodzie java.io.FileInputStream.readBytes.

Odtworzono to zarówno na 64-bitowym systemie Windows 7 (jdk1.6.0_30 64-bitowy, jdk1.7.0_03 64-bitowy), jak i w systemie Linux 2.6.18 (jdk1.6.0_21 32-bitowy). Kod ten to here i jest używany like this. Zobacz te linki do pełnego kodu - są tu ciekawe bity:

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

Ślady stosu wyglądać

"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 

lub

"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 

Z Sysinternals Process Explorer udało mi się uzyskać macierzyste ślady stosu tych wątków. Najczęściej, ponad 80% czasu, ślad stosu wygląda następująco:

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 

Dzieje się tak również dość często:

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 

A czasami to wykonywanie tej części kodu:

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 

Jakieś pomysły na rozwiązanie tego problemu? Czy jest to znany problem z JVM? Czy jest w pobliżu praca?

+3

Czy mógłbyś podać swój kod pętli? BTW warunek '> = 0' jest zbyt szeroki, jeśli' buf.length' jest różna od zera, wówczas read() gwarantuje odczyt co najmniej 1 bajtu lub return -1 (lub wyrzucenie wyjątku). –

+0

Co? Czy 'Process.getInputStream()' zwraca FileInputStream? –

+0

Tak. W java.lang.ProcessImpl # ProcessImpl można zobaczyć stdout_stream i stderr_stream zainicjalizowane za pomocą FileInputStream. To ma sens z punktu widzenia Uniksa, gdzie wszystko jest plikiem. –

Odpowiedz

1

Nie byłem jeszcze w stanie odtworzyć to lokalnie, ale dwa możliwe obejścia może być

  • Pobaw się z in.available().

  • przekierowania mocny i stderr w trakcie zewnętrznego gniazda i przeczytać od procesu sterowania zamiast.

+0

[Szybka korekta za pomocą in.available()] (http://code.google.com/p/pitestrunner/source/browse/pitest/src/main/java/org/pitest/util/StreamMonitor.java?spec = svn0607ac947dd76768f5e852350386bc9c324a6005 & r = 0607ac947dd76768f5e852350386bc9c324a6005 # 59) pomógł na razie uniknąć problemu. Wciąż szukamy lepszego rozwiązania i powód, dla którego tak się dzieje. Spróbuję wyprodukować [SSCCE] (http://sscce.org/) usuwając zbędny kod z projektu zamkniętego źródła, w którym mogę go odtworzyć. –

+0

Problem może zostać odtworzony przez awarię procesu potomnego z nieskończoną alokacją tablicy od http://stackoverflow.com/questions/65200/how-do-you-crash-a-jvm i prawdopodobnie inne wymienione tam techniki . – henry