2015-06-19 2 views
4

Моя программа java таинственно вылетает из-за NoClassDefFoundError. Тайна заключается в том, что сообщение об исключении указывает, что проблемный класс java/util/concurrent/CopyOnWriteArrayList$COWIterator, который является частью JRE. Исключение возникает после того, как другие классы JRE загружены без проблем.NoClassDefFoundError иногда для стандартного класса после System.out.close()

Исключение брошено из logback (версия 1.1.3), на первый взгляд, когда он итерация по списку appenders писать в (я предполагаю, что он использует CopyOnWriteArrayList для хранения списка appenders):

Thread [main] (Suspended (exception NoClassDefFoundError)) 
CopyOnWriteArrayList<E>.iterator() line: 959  
AppenderAttachableImpl<E>.appendLoopOnAppenders(E) line: 47 
Logger.appendLoopOnAppenders(ILoggingEvent) line: 273 
Logger.callAppenders(ILoggingEvent) line: 260 
Logger.buildLoggingEventAndAppend(String, Marker, Level, String, Object[], Throwable) line: 442 
Logger.filterAndLog_0_Or3Plus(String, Marker, Level, String, Object[], Throwable) line: 396 
Logger.info(String) line: 600 
MyProgramTextLogger.logStarting() line: 303 
MyProgram.run() line: 1686 
MyProgram.runProgram(MyProgram) line: 790 
MyProgram.main(String[]) line: 712 

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

Чтобы добавить к тайне эта проблема не возникает, если я запускаю программу на переднем плане или как процесс демона, управляемый systemd. Это проявляется только тогда, когда я запускаю свою программу в качестве дочернего процесса процесса демона, начатого systemd.

экспериментирования (с помощью static блока кода а) показало, что основной поток загрузчик классы могут загрузить CopyOnWriteArrayList$COWIterator класс ОК по программе запуска (то программа бросает NoClassDefFoundError для другого класса JRE). Это как если бы classloader решил потерять способность загружать классы.

Я не передаю никаких специальных аргументов программе java. Я использовал полное имя пути для опции java и предоставил параметр -showversion, чтобы гарантировать, что я использую одну и ту же JVM и JRE каждый раз. Моя командной строки просто:

/usr/bin/java \ 
-showversion \ 
-jar /home/myuser/lib/my-app.jar --1 --latest 

Java сообщает свою версию, чтобы быть

java version "1.7.0_75" 
OpenJDK Runtime Environment (rhel-2.5.4.2.el7_0-x86_64 u75-b13) 
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode) 

Моя программа не выполняет никакой явной загрузки класса, ни у меня есть пользовательский загрузчик классов: он использует по умолчанию класс загрузчика. Тем не менее, bkail указал, что это не имеет значения, если я это сделал, потому что классы платформы Java должны быть loaded by the boot class loader rather than the application class loader.

В момент, когда моя программа завершилась неудачно, мой код не создал ни одного потока. В моем коде не содержится никакого нативного кода, и, следовательно, нет «внешних» потоков, которые могли бы запутать вещи. Я считаю, что logback создает некоторые потоки, и, конечно же, есть обычные потоки демона Java.

И еще один таинственный аспект. В промежутке между созданием регистратора (с использованием org.slf4j.LoggerFactory.getLogger(MyProgram.class)) и вызывая метод Logger.info(String), который приводит к исключению, по существу все мое приложение выполняет закрытие стандартных выходных, стандартных выходных и стандартных выходных потоков ошибок, используя вызов этого метода:

private static void closeSystemStreams() { 
    try { 
     System.in.close(); 
    } catch (IOException e) { 
     // Do nothing; should never happen, and there is nothing we can do if 
     // it does 
    } 
    System.out.close(); 
    System.err.close(); 
} 

Если я опускаю этот код закрытия, программа работает нормально. Теперь я могу понять, что закрытие этих потоков может привести к ошибке при некоторых обстоятельствах (и some recommend against it), но почему это должно мешать загрузчику класса загрузки?

Что мне нужно сделать, чтобы гарантировать, что logback или JVM не будут иметь подобные проблемы с загрузкой?

+0

Пожалуйста, разместите реальную трассировку стека. У него нет никаких системных классов. Системные классы имеют имена системных пакетов. – EJP

+0

См. Также http://stackoverflow.com/questions/34413/why-am-i-getting-a-noclassdeffounderror-in-java – Raedwald

ответ

1

Похоже, что некоторые из кода библиотеки Java запутываются, если стандартные потоки закрыты. Возможно, где-то есть жестко закодированная проверка значения файлового дескриптора. Это может быть или не быть ошибкой, в зависимости от того, насколько велико вы интерпретируете поведение. Казалось бы безопаснее не закрывать стандартные потоки.

Смежные вопросы