6

Почему возникает ошибка NoClassDefFoundError в Java?

1

Я получаю ошибку NoClassDefFoundError при запуске моего Java-приложения. В чем обычно заключается причина этой ошибки?

5 ответ(ов)

10

Это может быть связано с несовпадением classpath во время компиляции и выполнения, но не обязательно.

Важно четко различать две или три разные исключения в этом случае:

  1. java.lang.ClassNotFoundException - это исключение указывает на то, что класс не был найден в classpath. Это означает, что мы пытались загрузить определение класса, но его не оказалось в classpath.

  2. java.lang.NoClassDefFoundError - это исключение указывает на то, что JVM искала определение класса в своей внутренней структуре данных, но не нашла его. Это отличается от ситуации, когда класс не может быть загружен из classpath. Обычно это указывает на то, что мы раньше пытались загрузить класс из classpath, но по какой-то причине это не удалось - теперь мы пытаемся снова использовать этот класс (и следовательно, нам нужно его загрузить, так как это не вышло в прошлый раз), но мы даже не собираемся пытаться его загрузить, потому что уже не удалось загрузить его ранее (и разумно подозреваем, что мы снова потерпим неудачу). Предыдущая ошибка может быть как ClassNotFoundException, так и ExceptionInInitializerError (указывает на сбой в статическом блоке инициализации) или может быть вызвана множеством других проблем. Главное, что NoClassDefFoundError не обязательно является проблемой с classpath.

3

Это происходит, когда существует файл класса, от которого зависит ваш код, и он присутствует во время компиляции, но не найден во время выполнения. Проверьте различия в classpath для времени сборки и времени выполнения.

0

Ошибка NoClassDefFound может возникать, когда код компилируется с использованием несовместимой версии класса, которая затем загружается во время выполнения. В моем случае это произошло при использовании библиотеки Apache Axis. У меня в пути классов было две версии этой библиотеки, и приложение подхватывало устаревшую и несовместимую версию, что и вызывало ошибку NoClassDefFound.

Это произошло в приложении командной строки, и я использовал подобную команду для установки пути классов:

set classpath=%classpath%;axis.jar

Чтобы устранить проблему и заставить приложение использовать правильную версию библиотеки, я поменял порядок в classpath следующим образом:

set classpath=axis.jar;%classpath%;

Теперь все работает корректно, и ошибка больше не возникает.

0

Одним из интересных случаев, когда вы можете столкнуться с множеством ошибок NoClassDefFoundError, является ситуация, когда вы:

  1. throw'ите RuntimeException в static блоке вашего класса Example
  2. Перехватываете это исключение (или если это вас не беспокоит, как в случае с тестами)
  3. Пытаетесь создать экземпляр этого класса Example
static class Example {
    static {
        thisThrowsRuntimeException();
    }
}

static class OuterClazz {

    OuterClazz() {
        try {
            new Example();
        } catch (Throwable ignored) { //симуляция перехвата RuntimeException из static блока
            // НИКОГДА НЕ ДЕЛАЙТЕ ЭТО В ПРОДАКШН-КОДЕ, ЭТО ТОЛЬКО ПРИМЕР для StackOverflow
        }

        new Example(); //это приведет к NoClassDefFoundError
    }
}

В данном случае NoClassDefError будет выброшено вместе с ExceptionInInitializerError из-за RuntimeException в статическом блоке.


Это особенно важный случай, когда вы сталкиваетесь с NoClassDefFoundErrors в ваших ЮНИТ-ТЕСТАХ.

В некотором смысле вы "делите" выполнение static блока между тестами, но исходная ExceptionInInitializerError будет только в одном тесте — в том, который использует проблемный класс Example. Другие тесты, которые также используют класс Example, будут просто выбрасывать NoClassDefFoundErrors.

0

Ошибка NoClassDefFoundError возникает, когда классы, загруженные классовым загрузчиком времени выполнения, не могут получить доступ к классам, уже загруженным корневым загрузчиком (root loader) Java. Поскольку различные загрузчики классов находятся в разных доменах безопасности (по определению Java), JVM не позволяет классам, уже загруженным корневым загрузчиком, быть разрешенными в пространстве адресов загрузчика времени выполнения.

Чтобы лучше понять, почему класс не может быть разрешен, вы можете использовать следующий подход: запустите вашу программу с помощью команды

java -javaagent:tracer.jar [ВАШИ java ПАРАМЕТРЫ]

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

Вот пример кода для ClassLoaderTracer.java, который можно использовать для трейсинга загрузки классов:

// ClassLoaderTracer.java
// Из: https://blogs.oracle.com/sundararajan/entry/tracing_class_loading_1_5

import java.lang.instrument.*;
import java.security.*;

// manifest.mf
// Premain-Class: ClassLoadTracer

// jar -cvfm tracer.jar manifest.mf ClassLoaderTracer.class

// java -javaagent:tracer.jar  [...]

public class ClassLoadTracer 
{
    public static void premain(String agentArgs, Instrumentation inst) 
    {
        final java.io.PrintStream out = System.out;
        inst.addTransformer(new ClassFileTransformer() {
            public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

                String pd = (null == protectionDomain) ? "null" : protectionDomain.getCodeSource().toString();
                out.println(className + " загружен " + loader + " в " + new java.util.Date() + " в " + pd);

                // вывод стека вызовов для потока, загружающего класс
                Thread.dumpStack();

                // мы просто хотим, чтобы оригинальные .class байты были загружены!
                // мы не модифицируем их...
                return null;
            }
        });
    }
}

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь