Java 中基于时间的 32 位溢出?还是 SLES11?

Java 中基于时间的 32 位溢出?还是 SLES11?

这是 Tomcat 6.0.18、Java 1.7.0_03(32 位)和 SLES11 SP2(64 位)。至于内核信息:

$ uname -a
Linux server-1 3.0.13-0.27-default #1 SMP Wed Feb 15 13:33:49 UTC 2012 (d73692b) x86_64 x86_64 x86_64 GNU/Linux

我们在三台服务器上进行了负载和寿命测试。在这三台独立的机器上,Tomcat 都退出了一秒每个 Tomcat 启动后 2^32 毫秒(49 天以上)的时间。在每台机器上,两个线程在 JVM 退出前生成堆栈跟踪(Tomcat 本身System.exit(1)在获取时会调用SocketTimeoutException,这就是 JVM 退出的原因)。

一个线程是(默认情况下)在端口 8005 上侦听关机命令的线程(通过查看 Tomcat 源代码进行验证):

Jun 22, 2012 9:10:15 AM org.apache.catalina.core.StandardServer await
SEVERE: StandardServer.await: accept: 
java.net.SocketTimeoutException: Accept timed out
      at java.net.PlainSocketImpl.socketAccept(Native Method)
      at java.net.AbstractPlainSocketImpl.accept(Unknown Source)
      at java.net.ServerSocket.implAccept(Unknown Source)
      at java.net.ServerSocket.accept(Unknown Source)
      at org.apache.catalina.core.StandardServer.await(StandardServer.java:389)
      at org.apache.catalina.startup.Catalina.await(Catalina.java:642)
      at org.apache.catalina.startup.Catalina.start(Catalina.java:602)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
      at java.lang.reflect.Method.invoke(Unknown Source)
      at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
      at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)

另一个线程(我们相信,尽管我们没有检查 Tomcat 源代码来验证)是处理传入端口 8080 连接的线程:

Jun 22, 2012 9:10:15 AM org.apache.jk.common.ChannelSocket acceptConnections
WARNING: Exception executing accept
java.net.SocketTimeoutException: Accept timed out
      at java.net.PlainSocketImpl.socketAccept(Native Method)
      at java.net.AbstractPlainSocketImpl.accept(Unknown Source)
      at java.net.ServerSocket.implAccept(Unknown Source)
      at java.net.ServerSocket.accept(Unknown Source)
      at org.apache.jk.common.ChannelSocket.accept(ChannelSocket.java:307)
      at org.apache.jk.common.ChannelSocket.acceptConnections(ChannelSocket.java:661)
      at org.apache.jk.common.ChannelSocket$SocketAcceptor.runIt(ChannelSocket.java:872)
      at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
      at java.lang.Thread.run(Unknown Source)

Tomcat 没有做任何疯狂的事情。在第一种情况下,它只是一个通过调用while (true)获得 a 的循环,并且调用失败。SocketServerSocket.accept()accept()

你知道为什么会发生这种情况吗?我可以尝试观察什么来找出如何在将来防止这种情况发生?

请注意,虽然雄猫运行了 2^32 毫秒,当 Tomcat 启动时系统已经启动。当然,这并不排除 Tomcat 启动时创建的某些进程变量参与其中。

答案1

我最近也看到了这个问题,它似乎与 Java 6 和 7 之间 32 位 Oracle JVM 中的更改有关。在 Linux 上,使用 strace 运行 32 位 Java 7 VM 时,当调用 ServerSocket.accept() 而没有设置 SO_TIMEOUT 时,会显示以下系统调用:

32369 poll([{fd=5, events=POLLIN|POLLERR}], 1, 4294967295 <unfinished ...>

对 poll() 的调用传递了 2^32 毫秒 (4294967295) 的超时值,而不是预期的负值(表示无限超时)。这最终导致 ServerSocket.accept() 抛出 SocketTimeoutException,从而导致 Tomcat 的引导代码执行服务器关闭。Tomcat 的这一特定部分永远不会期望 ServerSocket.accept 抛出 SocketTimeoutException。

如果可以操纵对 poll() 的调用,以便您不必等待 2^32 毫秒,则更容易重现此问题。这可以在 Linux 中通过覆盖 poll 系统调用来实现。执行此操作的一种方法是使用 LD_PRELOAD 指令加载覆盖版本的 poll。可以在以下位置找到一些展示此想法的示例代码https://github.com/vi/timeskew不幸的是,它不能覆盖轮询,但可以很容易地进行扩展。

答案2

我在 64 位 Debian Linux 上使用 Tomcat 7.0.35 和 Java 1.7.0_10(32 位)时遇到了同样的问题。

就我而言,更新并使用 64 位 JDK 解决了这个问题

相关内容