Знаете ли вы, сколько параметров может определять метод Java?

Java JVM

Первый: зачем изучать такую ​​скучную задачу

Я последние два дня читал старую книгу "Оранжевая реализация операционной системы" и наткнулся на давно забытую и давно не изученную операционную систему. При объяснении " защищенный режим» в Главе 3 автор упомянул шлюз вызова. Счетчик параметров в дескрипторе составляет всего 5 бит, то есть он поддерживает только до 32 параметров. мышление: В JVM метод Java может самое большее. Сколько параметров определено? Я знаю, что это очень скучный вопрос, даже если вы можете определить 10 000, 100 000, кто на самом деле будет это делать. Но разве не самое главное для кодера быть любопытным, а без любопытства какая разница между соленой рыбой?

Второе: экскурсии

Для такого рода проблем первым шагом, конечно же, является просмотр определения методов в JVM Здесь мы возьмем HotSpot в openJDK10 в качестве примера. В ConstMethod полем, представляющим количество параметров, является _size_of_parameters.

      u2                _size_of_parameters;         // size of the parameter block (receiver + arguments) in words   

Тип _size_of_parameters — u2.В JVM u2 имеет длину 2 байта.Теоретически максимальное количество параметров, поддерживаемых HotSpot, равно 2^16 - 1, что равно 65535.

Этот ответ правильный? Практикуйте правду!

Конечно, я не буду настолько глуп, чтобы определить 65 535 параметров один за другим, так что не стану ли я воспитателем в детском саду «сотни миллионов рисовых зернышек»? Кодер должен следовать методу Кодера:

    public static void main(String[] args) {
        for (int i = 0; i < 65535; i++) {
            System.out.print("int a" + i + ",");
        }
    }

Идеальное освобождение от продуктивности 😂 .

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

Разве это не 65535? Сколько это должно быть? Это один байт? Не много чепухи, я сразу пришел экспериментировать с 255 параметрами, компиляция прошла, и я попробовал еще раз 256, и выдается та же ошибка, что и 65535. Тогда результат очевиден, методы Java могут определять до 255 параметров.

Я проверил исходный код Javac, при генерации байт-кода метода есть ограничение на количество параметров метода:

    if (Code.width(types.erasure(env.enclMethod.sym.type).getParameterTypes()) + extras > ClassFile.MAX_PARAMETERS) {
        log.error(tree.pos(), "limit.parameters");
        nerrs++;
    }

где ClassFile.MAX_PARAMETERS = 255.

Мне очень не хочется сюда заходить.. HotSpot четко определяет количество параметров метода длиной в два байта.. Может быть, Javac ограничил его в процессе компиляции? Пока вы можете успешно скомпилировать метод Java с 256 параметрами, вы можете попробовать его в виртуальной машине, но как вы можете обойти Javac?

Я думаю, что в основном есть два пути:
1: Измените исходный код Javac, избавьтесь от вышеуказанных ограничений параметров, а затем перекомпилируйте;
Второе: используйте инструмент модификации байт-кода для жесткого изменения байт-кода, а также метод с 256 параметрами.

Первый способ кажется простым, но на самом деле проект Javac, извлеченный из openJDK, нельзя запустить напрямую, требуя большой настройки, а исходный код зависит от множества невидимых классов в jdk, что очень хлопотно в эксплуатации. Так что здесь я использую второй метод, инструмент — старый друг javassist.

На самом деле javassist очень прост в использовании, здесь мне просто нужно добавить новый метод в существующий файл класса:

        try {
            StringBuilder sb = new StringBuilder();

            sb.append("public static void testMax(");

            for (int i = 0; i < 256; i++) {
                sb.append("int a" + i);
                if(i < 255) {
                    sb.append(",");
                }
            }
            sb.append("){}");

            ClassPool cPool = new ClassPool(true);
            cPool.insertClassPath("/Users/wanginbeijing/Documents/MyProgramings/java/Mine/test/src");
            CtClass cClass = cPool.get("com.wangxiandeng.test.Test");
            CtMethod newMethod = CtNewMethod.make(sb.toString(), cClass);
            cClass.addMethod(newMethod);
            cClass.writeFile("/Users/wanginbeijing/Documents/MyProgramings/java/Mine/test/src");
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (CannotCompileException e) {
            e.printStackTrace();
        } catch (
                IOException e) {
            e.printStackTrace();
        }

Приведенное выше успешно добавило метод testMax() с 256 параметрами в файл Test.class через javassist. Теперь давайте попробуем это, запустив Test.class:

    java com.wangxiandeng.test.Test

Неожиданно на этот раз, хотя компилятор и был обманут, он не прошел уровень виртуальной машины, и операция прямо сообщает об ошибке:

错误: 加载主类 com.wangxiandeng.test.Test 时出现 LinkageError
        java.lang.ClassFormatError: Too many arguments in method signature in class file com/wangxiandeng/test/Test

Кажется, что Java ограничивает количество параметров метода не только во время компиляции, но и во время выполнения виртуальной машины. В духе проверки до конца я поискал в исходном коде HotSpot ошибку, о которой сообщается ниже, и нашел место, где виртуальная машина проверяет количество параметров:

Method* ClassFileParser::parse_method(const ClassFileStream* const cfs,
                                      bool is_interface,
                                      const ConstantPool* cp,
                                      AccessFlags* const promoted_flags,
                                      TRAPS) {
      ......
      if (_need_verify) {
            args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) +verify_legal_method_signature(name, signature, CHECK_NULL);

            if (args_size > MAX_ARGS_SIZE) {
                  classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_NULL);
            }
      }
      ......
}

Видно, что когда виртуальная машина анализирует метод в файле класса, она будет судить, больше ли количество параметров args_size, чем MAX_ARGS_SIZE, и если оно больше, будет сообщено об ошибке. MAX_ARGS_SIZE – 255.

Здесь следует отметить, что при вычислении args_size определяется, является ли метод статическим методом, если это не статический метод, он добавит единицу к числу исходных параметров метода, потому что нестатический метод добавит к параметру параметр по умолчанию.Верх списка: реальный исполнитель метода, то есть экземпляр объекта класса, к которому принадлежит метод.

В этот момент я наконец понял, что статические методы Java могут иметь только до 255 параметров, а нестатические методы могут иметь только до 254 параметров. Хотя это намного меньше, чем 65535, о которых я говорил в начале, этого вполне достаточно.В конце концов, осмелитесь ли вы определить в своем проекте метод с 255 параметрами, чтобы гарантировать, что вас не убьют?

Кто-то может спросить, а что, если параметр метода, который я определяю, является параметром переменной длины? Есть ли такой предел? Конечно, нет, потому что суть превращения в параметр заключается в том, чтобы фактически передать массив, сколько бы параметров вы ни передали, это фактически просто массив после компиляции.

все кончено

Ну вот, проделав эксперимент и написав статью, я наконец-то разобрался.Моя девушка уже крепко спала, как будто я совсем заскучал, как будто я действительно еще соленая рыба🐟.