Почему eclipse компилирует файл, который оболочка javac или mvn выдает ошибку: слишком большой код? - PullRequest
0 голосов
/ 24 мая 2018

Среда:

  • Затмение Луны
  • maven 3.2.3
  • Java 7

IЯ видел вопросы , подобные этому, но я не понимаю, почему Eclipse может скомпилировать этот файл, а мы не можем сделать это через оболочку.

Я также знаю, что этофайл это должен быть файл свойств, но это то, что я нашел в проекте.

Eclipse компилирует файл и создает байт-код Messeges.class, но ни javac, ни mvn не могли этого сделать, потому что error: codeслишком большой.

Этот файл Messages.java является перечислением, которое имеет 3232 строки и используется в качестве свойств для загрузки всего этого в память.Он содержит около 3100 различных элементов.

Messages.java

public enum Messages {
  MENU_MANAGEMENT("Management menu"),
  ...3100..
  MENU_OTHER("Other");

  private String name;

  private Messages(String name) {
    this.name = name;
  }
  ...
}

Ошибка

$javac Messages.java
Messages.java:11: error: code too large
        MENU_MANAGEMENT("Management menu"),
        ^
1 error

Как или почему Eclipse компилирует это?Я не могу понять, как затмение может это сделать ¿?

Ответы [ 3 ]

0 голосов
/ 24 мая 2018

Когда число констант enum больше 2000, eclipse генерирует синтетические методы, чтобы обойти ограничение размера кода.

См. https://bugs.eclipse.org/bugs/show_bug.cgi?id=331334

BTW: переключение на ecj в качестве компилятора вВаша сборка Maven также не является долгосрочным решением: это решение работает только до Java 9, поскольку инициализация конечного поля вне методов инициализации запрещена, поскольку https://bugs.java.com/view_bug.do?bug_id=JDK-8157181

0 голосов
/ 24 мая 2018

Чтобы сложить все уже упомянутые фрагменты.

Как уже сказал @ Joachim Sauer , существует ограничение длины байтового кода метода.см. JLS 4.9.1 .

Значение элемента code_length должно быть меньше 65536.

Допустим, что перечисление равно

public enum FooEnum {
    NAME_1("1"),
    NAME_2("2"),
    ...
    NAME_2442("2442");
    private final String value;
    FooEnum(String enumValue) {
        this.value = enumValue;
    }
    public String getValue() {
        return value;
    }
}

При использовании JDK 8 сгенерированный статический инициализатор будет

static {};
  Code:
     0: new           #4                  // class FooEnum
     3: dup
     4: ldc           #8                  // String NAME_1
     6: iconst_0
     7: ldc           #9                  // String 1
     9: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
    12: putstatic     #11                 // Field NAME_1:LFooEnum;
    ...
 65499: dup
 65500: sipush        2441
 65503: getstatic     #7334              // Field NAME_2442:LFooEnum;
 65506: aastore
 65507: putstatic     #1                 // Field $VALUES:[LFooEnum;
 65510: return

объявление enum NAME_2443 создаст еще 37 байт-кодов, что затем приведет к ошибке компиляции code to large.

Как сказал @ Till Brychcy , компилятор Eclipse работает вокруг ограничения размера, генерируя дополнительные методы, которые вызываются в статическом инициализаторе.

Статический инициализатор генерируется как

static {};
  Code:
     0: invokestatic  #2455               // Method " enum constant initialization$2":()V
     3: invokestatic  #2458               // Method " enum constant initialization$3":()V
     6: sipush        2442
     9: anewarray     #1                  // class FooEnum
    12: dup
    13: iconst_0
    14: getstatic     #2461               // Field NAME_1:LFooEnum;
    17: aastore
    ...

методы enum constant initialization$2 и enum constant initialization$3 инициализируют перечисления NAME_1 till NAME_2000 соответственно NAME_2001 till NAME_2442.

Чтобы попробовать самостоятельно создать следующие два файла

gen.sh

#!/bin/bash

mkdir -p src/main/java/
rm FooEnum.jdk FooEnum.ecj

(
echo "public enum FooEnum {"

number_of_enums=2442
i=1
while [ $i -lt $number_of_enums ]
do
  echo "    NAME_${i}(\"${i}\"),"
  i=$((i+1))
done
  echo "    NAME_${i}(\"${i}\");"

cat <<EOF
    private final String value;
    FooEnum(String value) {
        this.value = value;
    }
    public String getValue() {
        return value;
    }
}
EOF
) > src/main/java/FooEnum.java

mvn clean compile
javap -c -v -p target/classes/FooEnum.class > FooEnum.jdk

mvn clean compile -P ecj
javap -c -v -p target/classes/FooEnum.class > FooEnum.ecj

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>sub.optimal</groupId>
    <artifactId>ejc-demo</artifactId>
    <version>1.0</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--<maven.compiler.source>1.8</maven.compiler.source>-->
        <!--<maven.compiler.target>1.8</maven.compiler.target>-->
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <profile>
            <id>ecj</id>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>3.7.0</version>
                        <configuration>
                            <compilerId>eclipse</compilerId>
                        </configuration>
                        <dependencies>
                            <dependency>
                                <groupId>org.codehaus.plexus</groupId>
                                <artifactId>plexus-compiler-eclipse</artifactId>
                                <version>2.8.4</version>
                            </dependency>
                        </dependencies>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <id>jdk9</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>3.7.0</version>
                        <configuration>
                            <source>9</source>
                            <target>9</target>
                            <showWarnings>true</showWarnings>
                            <showDeprecation>true</showDeprecation>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

при запуске сценария gen.sh сгенерирует исходный файл enum FooEnum.java и затем скомпилирует его всначала с вашим текущим JDK, а затем с помощью компилятора Eclipse для Java.После каждой компиляции класс разбирается, а дизассемблированный байт-код сохраняется в файлах FooEnum.jdk и FooEnum.ecj.Для вашего дальнейшего расследования.

0 голосов
/ 24 мая 2018

Перечисляется со многими константами, особенно если они используют несколько аргументов конструктора, как правило, встречаются с этой ошибкой, потому что синтетический статический блок инициализатора становится слишком большим (размер кода ограничен примерно 64 КБ для метода).

Inв долгосрочной перспективе правильным решением будет рефакторинг вашего кода для уменьшения его размера.Либо разделив перечисление на несколько (возможно, используя общий интерфейс, чтобы иметь возможность продолжать использовать их согласованным образом), либо удалив аргумент (который купит вам еще несколько констант, но вы все равно столкнетесь с этой проблемой).

Тот факт, что компилятор Eclipse пока не жалуется, можно объяснить небольшими различиями в генерации кода.Он просто не достигает лимита , пока , но все равно будет достигать его (попробуйте добавить еще несколько сотен полей ...).

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

См. этот вопрос для подробного описания проблемы.и предлагаемые решения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...