Задачи Ant <javac> выдают исключение StackOverflowException

Я пытаюсь скомпилировать более 100 классов java из разных пакетов из чистого каталога (без дополнительных компиляций), используя следующие задачи ant:

<target name="-main-src-depend">
    <depend srcdir="${src.dir}" 
            destdir="${bin.dir}" 
            cache="${cache.dir}"
            closure="true"/>
</target>   

<target name="compile" depends="-main-src-depend"
        description="Compiles the project.">

    <echo>Compiling</echo>

    <javac  target="${javac.target}"
            source="${javac.source}"
            debug="${javac.debug}"
            srcdir="${src.dir}"
            destdir="${bin.dir}">
        <classpath>
            <path refid="runtime.classpath"/>
            <path refid="compile.classpath"/>
        </classpath>
    </javac>
</target>

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

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

Ответов (7)

Решение

Было бы неплохо узнать; что может вызвать или вызывает ошибку StackOverflowError во время компиляции кода Java?

Вероятно, что вычисление длинного выражения в вашем java-файле потребляет много памяти, и поскольку это выполняется вместе с компиляцией других классов, виртуальной машине просто не хватает места в стеке. Ваш сгенерированный класс, возможно, раздвигает допустимые пределы для своего содержимого. Смотрите главу 4.10 Ограничения виртуальной машины Java в спецификации виртуальной машины Java, Second Edition .

Исправление 1: рефакторинг класса

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

Исправление 2: увеличьте размер стека

Я думаю, что у Кирона есть одно решение, когда он упоминает аргумент -Xss. javac принимает ряд нестандартных аргументов, которые будут различаться в зависимости от версии и поставщика компилятора.

Мой компилятор:

$ javac -version
javac 1.6.0_05

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

javac -help
javac -X
javac -J-X

Я думаю, что ограничение стека для javac по умолчанию составляет 512 КБ. Вы можете увеличить размер стека для этого компилятора до 10 МБ с помощью этой команды:

javac -J-Xss10M Foo.java

Вы могли бы передать это в Ant-файле с элементом compilerarg, вложенным в вашу задачу javac .

<javac srcdir="gen" destdir="gen-bin" debug="on" fork="true">
    <compilerarg value="-J-Xss10M" />
</javac>

В некоторых других ответах упоминались исправления, требующие настройки fork="true", но другой вариант - увеличить пространство стека базовой JVM, созданной ant, путем установки ANT_OPTS переменной среды:

ANT_OPTS=-Xss10M ant
  <javac srcdir="gen" destdir="gen-bin" debug="on" fork="true">
      <compilerarg value="-J-Xss10M" />
    </javac>

из комментария выше неверно. Вам нужен пробел между -J и -X, например:

<javac srcdir="gen" destdir="gen-bin" debug="on" fork="true">
    <compilerarg value="-J -Xss10M" />
</javac>

чтобы избежать следующей ошибки:

 [javac] 
[javac] The ' characters around the executable and arguments are
[javac] not part of the command.
[javac] Files to be compiled:

... [javac] javac: недопустимый флаг: -J-Xss1m [javac] Использование: javac

Это происходит, когда вы запускаете команду javac из командной строки? Возможно, вы захотите попробовать атрибут fork .

Попробуйте добавить несколько вариантов этих атрибутов в строку задач Antjavac :

memoryinitialsize="256M" memorymaximumsize="1024M"

Вы также можете попробовать fork="true", не уверен, что это позволяет вам устанавливать значения для стека и кучи (также известный как -Xm1024), но это может помочь (если это будет работать из командной строки, но не в Ant).

[Edit]: добавлена ​​ссылка - на странице javacзадачи , казалось бы, предлагается, что параметры выше требуют, чтобы вы также установили fork="true" .

Это довольно странно, 100 классов на самом деле не так уж и много. Что делает компилятор при переполнении стека? Сгенерирована ли полезная трассировка стека? Что произойдет, если вы запустите javac прямо из командной строки, а не через ant?

Один из возможных обходных путей - просто увеличить размер стека с помощью -Xss аргумента JVM; либо к запущенной JVM, ant либо путем установки fork="true" и <compilerarg> в <javac> задаче. На самом деле теперь, когда я думаю об этом, проблема исчезнет, ​​просто вставив fork="true"?

Вот что я нашел. После отправки сообщения на мой вопрос , я пошел дальше и изменил задачу компиляции с атрибутами fork="true", memoryinitialsize="256m" и memorymaximumsize="1024m" (найденный сегодня , что это было предложено Kieron и jmanning2k, спасибо за ваше время). Тем не менее, это не решило проблему.

Я решил начать удаление классов из дерева исходных текстов, чтобы посмотреть, может ли он определить проблему. Оказывается, у нас был клиентский класс веб-службы для Axis 1.4, который был автоматически сгенерирован из файла WSDL. Теперь этот класс - монстр (как во Франкенштейне), он имеет 167 полевых членов (все они имеют тип String), 167 пар получателей / установщиков (по 1 для каждого поля), конструктор, который получает все 167 полей в качестве параметров, equals, который странным образом сравнивает все 167 полей. Для каждого поля сравнение происходит следующим образом:

(this.A == null && other.getA() == null) || (this.A != null && this.A.equals(other.getA()))

Результатом этого сравнения будет "добавлен" (&&) результат сравнения следующего поля и так далее. Класс продолжает метод hashCode, который также использует все поля, некоторые настраиваемые методы сериализации XML и метод, который возвращает объект метаданных, специфичный для оси, который описывает класс и который также использует все члены поля.

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

Теперь я знаю, что удаление этого единственного исходного файла решило проблему. Однако я совершенно не понимаю, почему именно этот класс вызвал проблему. Было бы неплохо узнать; что может вызвать или вызывает ошибку StackOverflowError во время компиляции кода Java? Думаю, я отправлю этот вопрос.

Для заинтересованных:

  • Windows XP с пакетом обновления 2 (SP2)
  • JDK 1.4.2_17 компании SUN
  • Муравей 1.7.0