Назад | Перейти на главную страницу

32-битное переполнение на основе времени в Java? Или 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 в секундочку 2 ^ 32 миллисекунды (49+ дней) с момента запуска каждого Tomcat. На каждой машине два потока производили трассировку стека до выхода из 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) цикл, который получает Socket позвонив ServerSocket.accept() и accept() вызовите бомбы.

Есть идеи, почему это происходит, и что я мог бы попытаться посмотреть / найти, чтобы выяснить, как предотвратить это в будущем?

Обратите внимание, что пока Кот работал в течение 2 ^ 32 миллисекунд, система уже была запущена, когда Tomcat был запущен. Конечно, это не исключает некоторую переменную процесса, которая была создана, когда Tomcat начал участвовать.

Я недавно также видел эту проблему, и она, похоже, связана с изменением, внесенным в 32-разрядную JVM Oracle между Java 6 и 7. В Linux при запуске 32-разрядной виртуальной машины Java 7 с помощью strace отображается следующий системный вызов когда 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 для загрузки замененной версии опроса. Пример кода, демонстрирующий эту идею, можно найти по адресу https://github.com/vi/timeskew. К сожалению, он не отменяет опрос, но может быть легко расширен для этого.

У меня возникла та же проблема при использовании Tomcat 7.0.35 с Java 1.7.0_10 (32 бит) на 64-битном Debian Linux.

В моем случае обновление и использование 64-битного JDK решило эту проблему.