2012-04-16 54 views
22

私は現在、1つの外部プロセスにつき2つのスレッドを持つプログラムをデバッグしています。これらの2つのスレッドはProcess.getErrorStream()およびProcess.getInputStream()while ((i = in.read(buf, 0, buf.length)) >= 0)ループを使用します。java.io.FileInputStream.readBytes(ネイティブメソッド)の無限100%CPU使用率

JVMクラッシュ(these hs_err_pid.log filesを参照)のために外部プロセスがクラッシュすると、その外部プロセスのstdout/stderrを読み取るスレッドが100%CPUを消費して終了しないことがあります。 ループボディが実行されていません(ここにロギングステートメントが追加されています)、無限ループはネイティブメソッドjava.io.FileInputStream.readBytesの内部にあるようです。

これは、Windows 7 64ビット(jdk1.6.0_30 64ビット、jdk1.7.0_03 64ビット)とLinux 2.6.18(jdk1.6.0_21 32ビット)の両方でこれを再現しました。問題のコードはhereで、like thisです。完全なコードのためにこれらのリンクを参照してください - ここで興味深いのビットです:

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

スタックトレースは、私がすることができたSysinternalsのプロセスエクスプローラで

"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 

または

"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 

のように見えますこれらのスレッドのネイティブスタックトレースを取得します。ほとんどの場合、時間の80%を超え、スタックトレースは次のようになります。

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 

また、これはかなり頻繁に起こる:

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 

そして、時にはそれは、コードのこの部分を実行します:

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 

どのようにこの問題を解決するためのアイデアですか?これはJVMの既知の問題ですか?回避策はありますか?

+3

ループコードを含めることができますか? '> = 0'の条件が過度に広い場合、' buf.length'がゼロでない場合、read()は少なくとも1バイトを読み込んだり、-1を返す(またはExceptionをスローする)ことが保証されます。 –

+0

何ですか? 'Process.getInputStream()'はFileInputStreamを返しますか? –

+0

はい。 java.lang.ProcessImpl#ProcessImplでは、stdout_streamとstderr_streamがFileInputStreamで初期化されていることがわかります。すべてがファイルであるUnixの観点からはちょっと意味があります。 –

答えて

1

私はまだローカルでこれを再現することができていませんでしたが、2つの回避策は

  • in.available()で遊んでみてくださいかもしれません。外部プロセスにおける

  • リダイレクトスタウトstderrのソケットへと 代わりに、制御プロセスからこれを読み取ります。

+0

[in.available()を使用したクイックフィックス](http://code.google.com/p/pitestrunner/source/browse/pitest/src/main/java/org/pitest/util/StreamMonitor.java?spec = svn0607ac947dd76768f5e852350386bc9c324a6005&r = 0607ac947dd76768f5e852350386bc9c324a6005#59)は、今のところ問題を回避するのに役立っています。私たちはまだ、より良いソリューションと、これが最初に起こっている理由を探しています。私はこれを再現できる閉じたソースプロジェクトから余計なコードを削除して[SSCCE](http://sscce.org/)を作成しようとします。 –

+0

問題は、子プロセスをhttp://stackoverflow.com/questions/65200/how-do-you-crash-a-jvmからの無限配列割り当てループでクラッシュさせて、おそらくそこにリストされている他のテクニックによって再生成することができます。 – henry