System.out.println в конечном итоге блокирует - PullRequest
7 голосов
/ 30 апреля 2011

У меня есть приложение, которое объединяет все свои журналы в класс одного экземпляра, чтобы упростить выборочную отладочную печать и т. Д. Класс работает около года или около того, работает без сбоев, но совсем недавно мне довелось перевернуть журнал уровень до самого высокого значения (что я редко делаю), и стандартный вывод , кажется, в конечном итоге блокирует . Это вызывает хаос в следующий раз, когда какая-то другая часть кода вызывает println, или когда он пытается использовать класс ведения журнала (который заблокирован в ожидании возврата println).

Сначала я подумал, что у меня возникла какая-то проблема параллелизма с моим классом журналирования, но очистка трассировки стека заверяет меня, что моя синхронизация работает правильно и что только один поток пытается записать данные в консоль в любой момент времени. , Вот пример трассировки стека в потоке-нарушителе:

Thread [Thread-127] (Suspended) 
    FileOutputStream.writeBytes(byte[], int, int) line: not available [native method] [local variables unavailable] 
    FileOutputStream.write(byte[], int, int) line: 260  
    BufferedOutputStream.flushBuffer() line: 65 
    BufferedOutputStream.flush() line: 123  
    PrintStream.write(byte[], int, int) line: 432   
    StreamEncoder.writeBytes() line: 202    
    StreamEncoder.implFlushBuffer() line: 272   
    StreamEncoder.flushBuffer() line: 85    
    OutputStreamWriter.flushBuffer() line: 168  
    PrintStream.write(String) line: 477 
    PrintStream.print(String) line: 619 
    PrintStream.println(String) line: 756   
    Logger(Logger).LogMessage(String) line: 180 
    RemoteConsoleMonitor$1.run() line: 56   
    Thread.run() line: 662  

Покопавшись немного глубже в собственных трассировках стека, я обнаружил, что процесс JVM блокирует собственный вызов ZwWriteFile Windows. Мое лучшее предположение - то, что JVM использует именованные каналы, настроенные для синхронного чтения / записи буферизованных данных, но что считыватель каким-то образом остановился и позволил заполнить буфер. Между тем, писатель блокируется, ожидая, пока освободится буферное пространство, но этого никогда не происходит. Вот собственная трассировка стека того же потока:

    ntdll.dll!_ZwWriteFile@36()  + 0x15 bytes   
    ntdll.dll!_ZwWriteFile@36()  + 0x15 bytes   
    jvm.dll!InterfaceSupport::serialize_memory(JavaThread * thread=0x00000220)  Line 40 + 0x9 bytes C++
    java.dll!writeBytes(const JNINativeInterface_ * * env=0x2ac2b13c, _jobject * this=0x3e00f204, _jobject * bytes=0x3e00f200, long off=0, long len=97, _jfieldID * fid=0x7ecee033)  Line 144 + 0x21 bytes  C
    java.dll!Java_java_io_FileOutputStream_writeBytes(const JNINativeInterface_ * * env=0x2ac2b13c, _jobject * this=0x3e00f204, _jobject * bytes=0x3e00f200, long off=0, long len=97)  Line 73 + 0x20 bytes C
0d8a3826()  
    ...

Конечный результат заключается в том, что буфер stdout заполняется, и рано или поздно вещи перестают записываться в консоль.

В этом и заключается подвох: если я запускаю приложение автономно, а не из скрипта сборки Apache Ant, я не могу воспроизвести проблему. Так что это может быть связано с тем, как Ant перенаправляет вывод процесса, но я не решаюсь представить отчет об ошибке, потому что кажется, что кто-то другой столкнулся бы с этой проблемой раньше ... и потому что я не могу получить то же самое поведение, чтобы воспроизвести в изоляции.

Есть какие-нибудь мысли или советы о том, как мне отследить это дальше?

ОБНОВЛЕНИЕ : Обновление Eclipse с 3.4.2 до Helios (M2) волшебным образом решило проблему. Я также могу продолжать использовать 3.4.2, запустив встроенный сборщик Ant из командной строки вместо пользовательского интерфейса:

java -jar <eclipse directory>\plugins\org.eclipse.equinox.launcher_<version>.jar -application org.eclipse.ant.core.antRunner -buildfile <buildfile>

1 Ответ

2 голосов
/ 30 апреля 2011

Что ж, это не решение, но некоторые рекомендации для дальнейшего изучения.

Согласно FAQ по Apache Ant

Когда Apache Ant разветвляется новыйобрабатывать, например, с помощью или задач, он также начнет чтение нового потока со стандартного ввода и отправит все, что он прочитал, этому процессу.,.

В следующих строках они упомянули проблему в Unix-подобных системах:

Такое поведение приводит к странным побочным эффектам, таким как Antпроцесс приостанавливается, когда в Unix-подобных системах в качестве фонового процесса запускается процесс сборки-разветвления .....

См. связанный Bug .

Шаги, которые вы можете предпринять сейчас:

  1. Обновление до последней версии Apache Ant
  2. Обновление до последней версии Java.
  3. Если возможно, примените последние исправления для вашегоОС
  4. Проверьте один раз, существует ли проблема при запуске приложения с помощью Ant
  5. Если Нет, то smile :)
  6. В противном случае попробуйте другой альтернативный способ.Попробуйте выполнить ваше приложение через Java ProcessBuilder или Runtime.getRuntime().exec(), то есть написать небольшой код запуска приложений и выполнить ваше приложение через него.
  7. Если ваше приложение работает, то проблема в Ant.Отправьте сообщение об ошибке.
  8. Если приложение по-прежнему ведет себя странно, вам нужно продолжить изучение кода или подумать о том, чтобы сообщить об ошибке Java.

Надеюсь, это поможет.Удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...