Моя программа 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 не будут иметь подобные проблемы с загрузкой?
Пожалуйста, разместите реальную трассировку стека. У него нет никаких системных классов. Системные классы имеют имена системных пакетов. – EJP
См. Также http://stackoverflow.com/questions/34413/why-am-i-getting-a-noclassdeffounderror-in-java – Raedwald