java.io.FileInputStream.readBytes(Native Method) 的无限 100% CPU 使用率

Posted

技术标签:

【中文标题】java.io.FileInputStream.readBytes(Native Method) 的无限 100% CPU 使用率【英文标题】:Infinite 100% CPU usage at java.io.FileInputStream.readBytes(Native Method) 【发布时间】:2012-04-28 00:30:34 【问题描述】:

我现在正在调试一个程序,它每个外部进程有两个线程,这两个线程使用while ((i = in.read(buf, 0, buf.length)) >= 0) 循环继续读取 Process.getErrorStream() 和 Process.getInputStream()。

有时当外部进程因 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) 
    ...

堆栈跟踪看起来像

"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

通过 Sysinternals Process Explorer,我能够获得这些线程的本机堆栈跟踪。大多数情况下,超过 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 的一个已知问题吗?有解决办法吗?

【问题讨论】:

能否包含您的循环代码?顺便说一句,&gt;= 0 条件过于宽泛,如果buf.length 不为零,则 read() 保证读取至少 1 个字节或返回 -1(或抛出异常)。 什么? Process.getInputStream() 是否返回 FileInputStream? 是的。在 java.lang.ProcessImpl#ProcessImpl 中,您可以看到 stdout_stream 和 stderr_stream 正在使用 FileInputStream 进行初始化。从 Unix 的角度来看,这有点道理,一切都是文件。 查看 OpenJDK 的源代码,readBytes 的实现没有循环,是对 Windows 的 ReadFile 函数的精简包装。让我们继续研究操作系统问题……也许是防病毒软件之类的…… 这个问题可能是相关的:weblogs.java.net/blog/kohsuke/archive/2009/09/28/… 【参考方案1】:

我还不能在本地重现这个,但两种可能的解决方法可能是

玩转in.available()

将外部进程中的 stoutstderr 重定向到套接字和 而是从控制进程中读取。

【讨论】:

A quick fix using in.available() 帮助暂时避免了这个问题。我们仍在寻找更好的解决方案以及首先发生这种情况的原因。我将尝试通过从封闭源代码项目中删除多余的代码来生成SSCCE,以便我可以重现它。 可以通过使用来自***.com/questions/65200/how-do-you-crash-a-jvm 的无限数组分配循环以及可能列出的其他技术使子进程崩溃来重新产生问题。

以上是关于java.io.FileInputStream.readBytes(Native Method) 的无限 100% CPU 使用率的主要内容,如果未能解决你的问题,请参考以下文章