Java asm类文件太大

Java asm类文件太大,java,java-bytecode-asm,Java,Java Bytecode Asm,在向类添加代码时,我遇到运行时异常“类文件太大!”由ClassWriter.toByteArray()触发 是否有解决此问题的方法?例如,如果一个方法太大,我们可以拆分它来解决问题,但是类呢 PS:使用asm 5.0.1 类读取器和类编写器的初始化 ClassReader classReader = new ClassReader(in); ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);

在向类添加代码时,我遇到运行时异常“类文件太大!”由
ClassWriter.toByteArray()
触发

是否有解决此问题的方法?例如,如果一个方法太大,我们可以拆分它来解决问题,但是类呢

PS:使用asm 5.0.1

类读取器和类编写器的初始化

    ClassReader classReader = new ClassReader(in);
    ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);

    MyClassVisitor myClassVisitor = new MyClassVisitor(Opcodes.ASM5, classWriter);
    classReader.accept(myClassVisitor, ClassReader.SKIP_DEBUG);
对于指令插入,我只是记录正在执行的指令,方法是在堆栈上按指令编号,并在每条指令后添加一个调用(每条指令的so+2)

当类文件大小太大时(如果类文件超过2GiB,这将非常奇怪),而当常量池中的项数超过65534时,不会引发
运行时异常(“类文件太大!”)

达到这个类文件限制仍然是相当不寻常的。因此,我建议重新检查您是否正在使用优化的仪器。当您将
ClassReader
传递给时,它将复制旧类的整个常量池,如果您只做一些小的更改,这是合适的

但是,如果您正在执行重大更改,例如重命名成员或类型,则在添加大量新条目的同时,常量池中可能会出现大量未使用的旧条目。不将读取器传递给编写器的构造函数会牺牲性能,但会创建一个只包含所需条目的新常量池。如果这还不够,您可以传递给
类读取器的构造函数来删除调试信息,从而进一步减少常量池项的数量

如果这仍然没有帮助,你必须重新设计你注射的东西。对于此类代码,适用于普通手写代码的规则相同。将代码拆分为大小合理的方法,重用公共代码,将方法组织在类中。毕竟,如果只注入已有方法的调用,即使代码注入本身也会变得更简单、更高效


问题是你的
super.visitldcinn(count)指令。这将为每个不同的整数值创建一个新的常量池条目。由于指令号可以是介于
0
65535
之间的任意整数,因此很容易超过可能的常量池条目数

对于该范围内的值,不需要使用常量池条目。有专门的字节码指令用于小整数值。但一般来说,创建一个实用方法,为每个
int
值创建最佳指令是值得的:

public final void push(final int value) {
    if(value >= -1 && value <= 5) {
        super.visitInsn(Opcodes.ICONST_0 + value);
    } else if(value == (byte)value) {
        super.visitIntInsn(Opcodes.BIPUSH, value);
    } else if(value == (short)value) {
        super.visitIntInsn(Opcodes.SIPUSH, value);
    } else {
        super.visitLdcInsn(value);
    }
}
公共最终无效推送(最终int值){
如果(value>=-1&&value当类文件太大时(如果类文件超过2GiB,这将非常奇怪),而当常量池中的项目数超过65534时,不会抛出
运行时异常(“类文件太大!”)

达到这个类文件限制仍然是很不寻常的。因此,我建议重新检查您是否使用了优化的检测。当您将
类读取器
传递给时,它将复制旧类的整个常量池,如果您只做一些小的更改,这是合适的

但是,如果您正在执行重大更改,例如重命名成员或类型,则在添加大量新条目的同时,常量池中可能会有大量未使用的旧条目。不将读取器传递给编写器的构造函数将牺牲性能,但只创建一个新的常量池,其中包含所需的条目。如果仅此一项无效,则n如果不够,您可以传递给
类读取器的构造函数以删除调试信息,从而进一步减少常量池项的数量

如果这仍然没有帮助,你必须重新设计你正在注入的代码。对于这类代码,同样的规则适用于普通手写代码。将代码拆分为合理大小的方法,重用普通代码,将方法组织在类中。毕竟,如果你只注入代码,即使代码注入本身也会变得更简单、更有效已经存在的方法的职业


问题是您的
super.visitldcinn(count);
指令。这将为每个不同的整数值创建一个新的常量池条目。由于指令编号可以是介于
0
65535
之间的任何整数,因此很容易超过可能的常量池条目数

对于该范围内的值,您不需要使用常量池条目。对于小整数值,有专用字节码指令。但通常,创建实用方法为每个
int
值创建最佳指令是值得的:

public final void push(final int value) {
    if(value >= -1 && value <= 5) {
        super.visitInsn(Opcodes.ICONST_0 + value);
    } else if(value == (byte)value) {
        super.visitIntInsn(Opcodes.BIPUSH, value);
    } else if(value == (short)value) {
        super.visitIntInsn(Opcodes.SIPUSH, value);
    } else {
        super.visitLdcInsn(value);
    }
}
公共最终无效推送(最终int值){

如果(value>=-1&&value如果你不提供任何详细信息,你怎么能指望任何人来帮助你呢?这个类一定是个怪物。你做了什么来积累这么大的一块代码?对不起,如果它缺少详细信息,我应该包括哪些其他信息?在检测它时导致此错误的类之一是org/apache/commons/lang3/builder/ToStringStyle.class(缺陷4j)74KB(原始大小)如果你不提供任何详细信息,你怎么能指望任何人来帮助你呢?这个类一定是个怪物。你做了什么来积累这么大的一大块代码?对不起,如果它没有详细信息,我应该包括哪些其他信息?在检测时导致此错误的类之一是org/apache/commons/lang3/builder/ToStringStyle.class(缺陷4J)74KB(原始大小)感谢您的指导性回答。我添加了SKIP_DEBUG标志,但仍然得到异常。我修改了我的问题,以反映更多我正在尝试做的事情(记录正在执行的每条指令)。谢谢你的指导性回答。我添加了SKIP_DEBUG标志,但仍有例外。我已修改了我的问题