web-dev-qa-db-ja.com

Javaでの時間ベースの32ビットオーバーフロー?またはSLES11?

これはTomcat6.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
_

3台のサーバーで負荷と寿命のテストを行っていました。 3つの別々のマシンすべてで、各Tomcatが起動してから2 ^ 32ミリ秒(49日以上)以内にTomcatが終了しました1秒。各マシンで、2つのスレッドがJVMが終了する前にスタックトレースを生成しました(Tomcat自体がSocketTimeoutExceptionを取得するとSystem.exit(1)を呼び出すため、JVMが終了します)。

1つのスレッドは、(デフォルトで)ポート8005でshutdownコマンドをリッスンするスレッドです(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)
_

もう1つのスレッドは(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)を呼び出してSocketを取得するのはServerSocket.accept()ループであり、accept()は爆弾を呼び出します。

なぜこれが起こっているのか、そして将来それを防ぐ方法を理解するために私が見たり探したりすることができるものはありますか?

Tomcatが2 ^ 32ミリ秒実行されている間、Tomcatが起動されたときにシステムはすでに起動していたことに注意してください。もちろん、Tomcatが関与し始めたときに作成されたプロセス変数を除外するものではありません。

4
QuantumMechanic

最近この問題も確認しましたが、32ビットのOracleJVMで行われたJava 6と7の間の変更に限定されているようです。Linuxでは、32ビット= Java 7 VM with straceは、SO_TIMEOUTを設定せずにServerSocket.accept()が呼び出された場合の次のシステムコールを示しています。

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

Poll()の呼び出しは、無限のタイムアウトを示す予想される負の値ではなく、2 ^ 32ミリ秒(4294967295)のタイムアウト値を渡します。これにより、最終的にServerSocket.accept()がSocketTimeoutExceptionをスローし、Tomcatのbootstrapコードがサーバーのシャットダウンを実行します。Tomcatのその特定の部分は、ServerSocketによってSocketTimeoutExceptionがスローされることを予期しません。受け入れる。

2 ^ 32ミリ秒待つ必要がないように、poll()の呼び出しを操作できると、この問題を再現するのが簡単になります。これは、Linuxでpollシステムコールをオーバーライドすることで実行できます。これを行う1つの方法は、LD_PRELOADディレクティブを使用してオーバーライドされたバージョンのポーリングをロードすることです。このアイデアを示すいくつかのサンプルコードは https://github.com/vi/timeskew にあります。残念ながら、それはポーリングをオーバーライドしませんが、そうするために容易に拡張することができます。

4
mike

64ビットのDebianLinuxでTomcat7.0.35をJava 1.7.0_10(32ビット)で使用すると、同じ問題が発生していました。

私の場合、64ビットJDKを更新して使用すると、この問題が解決しました

3
stefan