Первый: зачем изучать такую скучную задачу
Я последние два дня читал старую книгу "Оранжевая реализация операционной системы" и наткнулся на давно забытую и давно не изученную операционную систему. При объяснении " защищенный режим» в Главе 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 параметрами, чтобы гарантировать, что вас не убьют?
Кто-то может спросить, а что, если параметр метода, который я определяю, является параметром переменной длины? Есть ли такой предел? Конечно, нет, потому что суть превращения в параметр заключается в том, чтобы фактически передать массив, сколько бы параметров вы ни передали, это фактически просто массив после компиляции.
все кончено
Ну вот, проделав эксперимент и написав статью, я наконец-то разобрался.Моя девушка уже крепко спала, как будто я совсем заскучал, как будто я действительно еще соленая рыба🐟.