NoClassDefFoundError в JFace FontRegistry
Когда я запускаю приложение SWT (через профиль запуска Eclipse), я получаю следующую трассировку стека:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jface/resource/FontRegistry
at org.eclipse.jface.resource.JFaceResources.getFontRegistry(JFaceResources.java:338)
at org.eclipse.jface.window.Window.close(Window.java:313)
at org.eclipse.jface.dialogs.Dialog.close(Dialog.java:971)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.close(ProgressMonitorDialog.java:348)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.finishedRun(ProgressMonitorDialog.java:582)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:498)
at com.blah.si.workflow.SWTApplication.main(SWTApplication.java:135)
Теперь о вещах, которые делают это странным:
- Когда я меняю путь сборки проекта и заменяю jface.jar исходным проектом (та же версия - 3.3.1), ошибка исчезает.
- Другие приложения, которые у меня есть, которые используют ту же банку и копию того же профиля запуска и проекта, все работают нормально.
- Это НЕ
ClassNotFoundException
. Класс находится в пути к классам. Если я прикреплю источник к банке, я могу выполнить отладку в методе getFontRegistry. Метод будет успешно выполнен несколько раз, прежде чем в конечном итоге будет выданNoClassDefFoundError
строка 338. Строка 337 - это оператор «if variable == null», проверяющий, была ли инициализирована статическая переменная. Строка 338 инициализирует его, если он еще не инициализирован. В первый раз проверка нуля не выполняется, и выполняется инициализация. При последующих проходах через метод проверка на null проходит, и, таким образом, возвращается уже инициализированное статическое значение. На последнем проходе (тот, который завершился неудачей) проверка нуля снова терпит неудачу (даже если статическая переменная уже была инициализирована), и когда он пытается повторно инициализировать статическую переменную,NoClassDefFoundError
брошен. Вот соответствующий источник (начиная со строки 336 обратите внимание, что fontRegistry - это частная статическая переменная, которая не устанавливается ни в каком другом месте):
.
public static FontRegistry getFontRegistry() {
if (fontRegistry == null) {
fontRegistry = new FontRegistry(
"org.eclipse.jface.resource.jfacefonts");
}
return fontRegistry;
}
.
- Я уже получил новую копию jar-файла (чтобы убедиться, что он не поврежден) удалил мои файлы .classpath и .project, запустил новый проект и воссоздал профиль запуска. Без изменений.
Из-за особенностей в пункте 3 выше, я подозреваю какое-то странное поведение загрузчика классов - кажется, что последний проход через метод находится в другом загрузчике классов?
Идеи?
Обновление: ответ, предоставленный Pourquoi Litytestdata, побудил меня обратить внимание на то, что происходит в блоке try чуть выше строки 458 ProgressMonitorDialog. Действительно, этот код генерировал исключение, которое было поглощено блоком finally. Основной причиной был ДРУГОЙ отсутствующий класс (отсутствующим классом был не JFontRegistry или какой-либо из его непосредственно связанных классов, а другой, который зависел от паутины в пограничном случае.) Я поддерживаю все ответы, указывающие мне обратить внимание на путь к классам , и принятие Pourquoi's, потому что это был прорыв. Спасибо всем.
Ответов (5)5
Я думаю, что трассировка стека, представленная выше, скрывает настоящую проблему. Ниже приведен код метода run
в
org.eclipse.jface.dialogs.ProgressMonitorDialog (с добавленным мной комментарием):
public void run(boolean fork, boolean cancelable,
IRunnableWithProgress runnable) throws InvocationTargetException,
InterruptedException {
setCancelable(cancelable);
try {
aboutToRun();
// Let the progress monitor know if they need to update in UI Thread
progressMonitor.forked = fork;
ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
.getDisplay());
} finally {
finishedRun(); // this is line 498
}
}
Вторая снизу строка в трассировке стека Джареда - это строка 498 этого класса, которая является вызовом finishedRun()
внутри finally
блока. Я подозреваю, что настоящая причина - исключение, возникшее в try
блоке. Поскольку код в finally
блоке также вызывает исключение, исходное исключение теряется.
Чтобы добавить к отличному ответу TofuBeer , поскольку NoClassDefFoundError
указывает, что:
- класс был найден самым ,
org.eclipse.jface.resource.FontRegistry
ClassLoader
- но не может быть загружен, не вызывая ошибки, например, наличие статических блоков или членов, использующих объект
Class
, не найденный вClassLoader
.
Посмотрим на org.eclipse.jface.resource.FontRegistry
исходный код :
У него нет статической инициализации переменной (как и у его суперклассов).
Посмотрим на org.eclipse.jface.resource.JFaceResources
исходный код
getFontRegistry()
Функция , в которой ошибка срабатывает использует переменный статический fontRegistry :
/**
* The JFace font registry; <code>null</code> until lazily initialized or
* explicitly set.
*/
private static FontRegistry fontRegistry = null;
Таким образом, напрашивается поднимает на вопрос : почему статическая переменная инициализируется внезапно рассматриваться null
снова?
Потому что как-нибудь FontRegistry
или JFaceResources
выгрузится гк ?!
Если поле объявлено статическим, существует ровно одно воплощение поля, независимо от того, сколько экземпляров (возможно, ноль) класса в конечном итоге может быть создано. Статическое поле, иногда называемое переменной класса, воплощается при инициализации класса (§12.4).
Таким образом, не имеет значения, существуют ли экземпляры класса в любое время, поле будет существовать до тех пор, пока сам класс будет загружен.
Если бы это был плагин eclipse , это могло быть связано с этой записью в FAQ
Вот типичный сценарий для нового пользователя:
вы пишете подключаемый модуль, расширяющий подключаемый модуль XYZ.
Чтобы заставить его скомпилировать, вы добавляете ссылку на JAR-файл для плагина XYZ в путь сборки вашего проекта либо со страницы свойств Java Build Path, либо путем редактирования файла .classpath.
При запуске среды выполнения верстака, сообщаются следующая удивительная ошибка:java.lang.NoClassDefFoundError: XYZ.SomeClass
.Не просматривайте вкладку «Плагины и фрагменты» в конфигурации запуска рабочей среды выполнения.
Эта вкладка влияет только на то, какие подключаемые модули используются для вашей рабочей среды выполнения и загружаются ли они из рабочей области или из каталога установки Eclipse.Вместо этого начните искать в манифесте подключаемого модуля.
Отредактируйте файл plugin.xml и убедитесь, что XYZ указан как обязательный плагин .
Затем сохраните файл plugin.xml.
Это автоматически обновит путь сборки проекта.Никогда не редактируйте файл .classpath вручную при написании подключаемого модуля.
Плагин Manifest Editor просто перезаписывает любые внесенные в него изменения. Не очень цивилизованно, но так работает.
Похоже, вам не хватает файла JAR, который содержит зависимость, как упоминалось в этой записи блога от июля 2006 года, написанной Сандживом ДЖИВАНОМ :
Разница между ClassNotFoundException и NoClassDefFoundError
A
ClassNotFoundException
выдается, когда сообщаемый класс не найден ClassLoader.
Обычно это означает, что класс отсутствует вCLASSPATH
.
Это также может означать, что рассматриваемый класс пытается загрузить из другого класса, который был загружен в родительский,ClassLoader
и, следовательно, класс из дочернегоClassLoader
не отображается.
Иногда это происходит при работе в более сложных средах, таких как сервер приложений (WebSphere печально известна такимиClassLoader
проблемами).Люди часто путают
java.lang.NoClassDefFoundError
сjava.lang.ClassNotFoundException
. Однако есть важное различие.Например, исключение (на самом деле ошибка, поскольку
java.lang.NoClassDefFoundError
является подклассомjava.lang.Error
), например
java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory
не означает, что класса ActiveMQConnectionFactory нет в
CLASSPATH
.На самом деле все совсем наоборот.
Это означает, что класс
ActiveMQConnectionFactory
был найден,ClassLoader
однако при попытке загрузить класс возникла ошибка при чтении определения класса.Обычно это происходит, когда рассматриваемый класс имеет статические блоки или члены, которые используют класс, который не найден
ClassLoader
.Итак, чтобы найти виновника, просмотрите источник рассматриваемого класса (
ActiveMQConnectionFactory
в данном случае) и найдите код, используя статические блоки или статические члены .
Если у вас нет доступа к источнику, просто декомпилируйте его с помощью JAD .Изучив код, скажем, вы нашли строку кода, как показано ниже, убедитесь, что класс SomeClass в вашем
CLASSPATH
.
private static SomeClass foo = new SomeClass();
Совет: Чтобы узнать, к какому jar- файлу принадлежит класс, вы можете использовать jarFinder веб-сайта . Это позволяет указать имя класса с использованием подстановочных знаков и искать класс в своей базе данных jar-файлов.
jarhoo позволяет вам делать то же самое, но его больше нельзя использовать бесплатно.Если вы хотите определить, к какому jar-файлу принадлежит класс, на локальном пути, вы можете использовать такую утилиту, как jarscan . Вы просто указываете класс, который хотите найти, и путь к корневому каталогу, в котором вы хотите начать поиск класса в файлах jar и zip.
Чтобы лучше понять, связана ли проблема с загрузчиком классов, просмотрите код, в котором он работает, и добавьте:
try
{
final Class clazz;
final ClassLoader loader;
clazz = Class.forName("org/eclipse/jface/resource/FontRegistry");
loader = clazz.getClassLoader();
System.out.println("The classloader at step 1 is: " + loader);
}
catch(final Throwable ex)
{
ex.printStackTrace();
}
А затем сделайте то же самое, когда вы получаете NoClassDefFoundError, и посмотрите, отличаются ли загрузчики классов.
Тогда вы сможете убедиться, что это ClassLoader, который отличается. Вы можете сообщить, что с этим происходит? В зависимости от результата у меня может быть больше идей.