ASM Type top (current frame, locals[3]) is not assignable 问题排查

好久不见. 提交于 2019-12-25 06:19:32

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

    先说遇到的问题,Hasor 在1.6 和早期 1.7 jdk 下运行 asm 动态生成的字节码无任何异常。接着有反馈在 java8下出现类似如下异常,一开始并没有里会这个异常信息。找到一个解决办法是增加一个  -noverify 参数来压制这个校验异常。

    最近比较好奇想寻找一下究竟为什么会出现 VerifyError 错误,下面是这个错误的全貌。


2017-02-15 11:03:47.423 [RSF-Nio-4] INFO  - connected form 127.0.0.1:60618
2017-02-15 11:03:47.440 [RSF-Biz-1] ERROR - do request(12345) failed -> service [RSF]net.hasor.website.client.ProjectService-1.0.0, error :(net.hasor.core.utils.UnhandledException)java.lang.VerifyError - Inconsistent stackmap frames at branch target 101
Exception Details:
  Location:
    net/hasor/website/manager/UserManager$A_19.loginUpdate(Lnet/hasor/website/domain/UserDO;Ljava/lang/String;)V @101: new
  Reason:
    Type top (current frame, locals[3]) is not assignable to 'java/lang/Throwable' (stack map, locals[3])
  Current Frame:
    bci: @92
    flags: { }
    locals: { 'net/hasor/website/manager/UserManager$A_19', 'net/hasor/website/domain/UserDO', 'java/lang/String', top, top, 'java/lang/Throwable' }
    stack: { integer }
  Stackmap Frame:
    bci: @101
    flags: { }
    locals: { 'net/hasor/website/manager/UserManager$A_19', 'net/hasor/website/domain/UserDO', 'java/lang/String', 'java/lang/Throwable' }
    stack: { }
  Bytecode:
    0x0000000: 1002 bd00 0659 1000 1208 5359 1001 120a
    0x0000010: 533a 0410 02bd 000c 5910 002b 5359 1001
    0x0000020: 2c53 3a05 2ab6 0012 1213 1904 b600 173a
    0x0000030: 06bb 0019 5919 062a 1905 b700 1d3a 07bb
    0x0000040: 001f 5912 2119 0619 07b7 0024 b600 283a
    0x0000050: 0819 0857 b13a 0519 05c1 002a 9900 0919
    0x0000060: 05c0 002a bfbb 002a 5919 05b7 002d bf  
  Exception Handler Table:
    bci [0, 83] => handler: 85
  Stackmap Table:
    same_locals_1_stack_item_extended(@85,Object[#4])
    append_frame(@101,Object[#4])

net.hasor.core.utils.UnhandledException: java.lang.VerifyError - Inconsistent stackmap frames at branch target 101
Exception Details:
  Location:
    net/hasor/website/manager/UserManager$A_19.loginUpdate(Lnet/hasor/website/domain/UserDO;Ljava/lang/String;)V @101: new
  Reason:
    Type top (current frame, locals[3]) is not assignable to 'java/lang/Throwable' (stack map, locals[3])
  Current Frame:
    bci: @92
    flags: { }
    locals: { 'net/hasor/website/manager/UserManager$A_19', 'net/hasor/website/domain/UserDO', 'java/lang/String', top, top, 'java/lang/Throwable' }
    stack: { integer }
  Stackmap Frame:
    bci: @101
    flags: { }
    locals: { 'net/hasor/website/manager/UserManager$A_19', 'net/hasor/website/domain/UserDO', 'java/lang/String', 'java/lang/Throwable' }
    stack: { }
  Bytecode:
    0x0000000: 1002 bd00 0659 1000 1208 5359 1001 120a
    0x0000010: 533a 0410 02bd 000c 5910 002b 5359 1001
    0x0000020: 2c53 3a05 2ab6 0012 1213 1904 b600 173a
    0x0000030: 06bb 0019 5919 062a 1905 b700 1d3a 07bb
    0x0000040: 001f 5912 2119 0619 07b7 0024 b600 283a
    0x0000050: 0819 0857 b13a 0519 05c1 002a 9900 0919
    0x0000060: 05c0 002a bfbb 002a 5919 05b7 002d bf  
  Exception Handler Table:
    bci [0, 83] => handler: 85
  Stackmap Table:
    same_locals_1_stack_item_extended(@85,Object[#4])
    append_frame(@101,Object[#4])

	at net.hasor.core.utils.ExceptionUtils.toRuntimeException(ExceptionUtils.java:28) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.createObject(TemplateBeanBuilder.java:159) ~[classes/:na]
	at net.hasor.core.container.BeanContainer.callSuperCreateObject(BeanContainer.java:156) ~[classes/:na]
	at net.hasor.core.container.BeanContainer.access$000(BeanContainer.java:42) ~[classes/:na]
	at net.hasor.core.container.BeanContainer$1.get(BeanContainer.java:144) ~[classes/:na]
	at net.hasor.core.provider.SingleProvider.get(SingleProvider.java:35) ~[classes/:na]
	at net.hasor.core.container.BeanContainer.createObject(BeanContainer.java:142) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.getDefaultInstance(TemplateBeanBuilder.java:88) ~[classes/:na]
	at net.hasor.core.context.TemplateAppContext$1.get(TemplateAppContext.java:149) ~[classes/:na]
	at net.hasor.core.context.TemplateAppContext.getInstance(TemplateAppContext.java:98) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.injInject(TemplateBeanBuilder.java:249) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.injectObject(TemplateBeanBuilder.java:232) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.doInject(TemplateBeanBuilder.java:176) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.createObject(TemplateBeanBuilder.java:149) ~[classes/:na]
	at net.hasor.core.container.BeanContainer.callSuperCreateObject(BeanContainer.java:156) ~[classes/:na]
	at net.hasor.core.container.BeanContainer.access$000(BeanContainer.java:42) ~[classes/:na]
	at net.hasor.core.container.BeanContainer$1.get(BeanContainer.java:144) ~[classes/:na]
	at net.hasor.core.provider.SingleProvider.get(SingleProvider.java:35) ~[classes/:na]
	at net.hasor.core.container.BeanContainer.createObject(BeanContainer.java:142) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.getDefaultInstance(TemplateBeanBuilder.java:88) ~[classes/:na]
	at net.hasor.core.context.TemplateAppContext$1.get(TemplateAppContext.java:149) ~[classes/:na]
	at net.hasor.core.context.TemplateAppContext.getInstance(TemplateAppContext.java:98) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.injInject(TemplateBeanBuilder.java:249) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.injectObject(TemplateBeanBuilder.java:232) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.doInject(TemplateBeanBuilder.java:176) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.createObject(TemplateBeanBuilder.java:149) ~[classes/:na]
	at net.hasor.core.container.BeanContainer.callSuperCreateObject(BeanContainer.java:156) ~[classes/:na]
	at net.hasor.core.container.BeanContainer.createObject(BeanContainer.java:148) ~[classes/:na]
	at net.hasor.core.container.TemplateBeanBuilder.getDefaultInstance(TemplateBeanBuilder.java:88) ~[classes/:na]
	at net.hasor.core.context.TemplateAppContext$1.get(TemplateAppContext.java:149) ~[classes/:na]
	at net.hasor.core.context.TemplateAppContext.getInstance(TemplateAppContext.java:98) ~[classes/:na]
	at net.hasor.core.provider.ClassAwareProvider.get(ClassAwareProvider.java:40) ~[classes/:na]
	at net.hasor.rsf.rpc.caller.remote.RsfInvokeFilterChain.doFilter(RsfInvokeFilterChain.java:40) ~[classes/:na]
	at net.hasor.rsf.rpc.caller.RsfFilterHandler.doFilter(RsfFilterHandler.java:43) ~[classes/:na]
	at net.hasor.rsf.filters.online.OnlineRsfFilter.doFilter(OnlineRsfFilter.java:37) ~[classes/:na]
	at net.hasor.rsf.rpc.caller.RsfFilterHandler.doFilter(RsfFilterHandler.java:41) ~[classes/:na]
	at net.hasor.rsf.filters.thread.LocalWarpFilter.doFilter(LocalWarpFilter.java:32) ~[classes/:na]
	at net.hasor.rsf.rpc.caller.RsfFilterHandler.doFilter(RsfFilterHandler.java:41) ~[classes/:na]
	at net.hasor.rsf.filters.local.LocalPref.doFilter(LocalPref.java:49) ~[classes/:na]
	at net.hasor.rsf.rpc.caller.RsfFilterHandler.doFilter(RsfFilterHandler.java:41) ~[classes/:na]
	at net.hasor.rsf.filters.trace.TraceFilter.doFilter(TraceFilter.java:40) ~[classes/:na]
	at net.hasor.rsf.rpc.caller.RsfFilterHandler.doFilter(RsfFilterHandler.java:41) ~[classes/:na]
	at net.hasor.rsf.rpc.caller.remote.InvokerProcessing.run(InvokerProcessing.java:152) ~[classes/:na]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_74]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_74]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_74]
Caused by: java.lang.VerifyError: Inconsistent stackmap frames at branch target 101
Exception Details:
  Location:
    net/hasor/website/manager/UserManager$A_19.loginUpdate(Lnet/hasor/website/domain/UserDO;Ljava/lang/String;)V @101: new
  Reason:
    Type top (current frame, locals[3]) is not assignable to 'java/lang/Throwable' (stack map, locals[3])
  Current Frame:
    bci: @92
    flags: { }
    locals: { 'net/hasor/website/manager/UserManager$A_19', 'net/hasor/website/domain/UserDO', 'java/lang/String', top, top, 'java/lang/Throwable' }
    stack: { integer }
  Stackmap Frame:
    bci: @101
    flags: { }
    locals: { 'net/hasor/website/manager/UserManager$A_19', 'net/hasor/website/domain/UserDO', 'java/lang/String', 'java/lang/Throwable' }
    stack: { }
  Bytecode:
    0x0000000: 1002 bd00 0659 1000 1208 5359 1001 120a
    0x0000010: 533a 0410 02bd 000c 5910 002b 5359 1001
    0x0000020: 2c53 3a05 2ab6 0012 1213 1904 b600 173a
    0x0000030: 06bb 0019 5919 062a 1905 b700 1d3a 07bb
    0x0000040: 001f 5912 2119 0619 07b7 0024 b600 283a
    0x0000050: 0819 0857 b13a 0519 05c1 002a 9900 0919
    0x0000060: 05c0 002a bfbb 002a 5919 05b7 002d bf  
  Exception Handler Table:
    bci [0, 83] => handler: 85
  Stackmap Table:
    same_locals_1_stack_item_extended(@85,Object[#4])
    append_frame(@101,Object[#4])

	at java.lang.Class.getDeclaredConstructors0(Native Method) ~[na:1.8.0_74]
	at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671) ~[na:1.8.0_74]
	at java.lang.Class.getConstructor0(Class.java:3075) ~[na:1.8.0_74]
	at java.lang.Class.getConstructor(Class.java:1825) ~[na:1.8.0_74]
	at net.hasor.core.container.TemplateBeanBuilder.createObject(TemplateBeanBuilder.java:142) ~[classes/:na]
	... 44 common frames omitted

Process finished with exit code 137 (interrupted by signal 9: SIGKILL)

----------

    Hasor 的 Aop 功能是通过 asm 进行动态代理,每个被代理的方法会用如下方式生成。一般情况下这个生成的类是在内存中的,本地磁盘是看不到它的。为了排查框架问题,在 Hasor 3.1.0 版本之后添加了 “WORK_MODE” 环境变量参数,将其设置为 debug。

    之后所有被 Hasor 动态代理的类都会 把生成的 class 保存到 work 目录下的 temp 目录中了。 

    现在看一下 Hasor aop 动态代理方法的全貌把。这个asm 生成动态类的 java 反编译,目测生成的代码没有什么异常。

----------

    既然代码反编译成 java 源文件看不出端倪,那我们就直接 “javap -p -c ”看一下字节码指令上不同的地方。javap 反编译,生成的代码(右侧)和java8下编译的代码(左侧)。经过字节码反编译似乎看出了端倪,难道是因为 return 指令强制后置的问题?

----------

    既然出现问题的代码在 catch 块中,那就把 异常抛出的生成代码简化一下。把上面的代码封装到一个工具类里然后去生成一个更为简单的 throw 语句,如下:

    问题解决。

----------

总结一下:虽然最后引发原因的异常指令暂时没找到,但是问题搞定了。最后原因定位在生成的 if 代码问题上。以后有时间在 深入一下 jvm 源码看看 jdk8 加了什么新东西。

----------

最后更新

问题在大约 1 年前解决了,近看看到这个 blog 顺手更新一下。  问题是字节码生成指令结果不够严谨。 和 jdk 版本无关系,需要加入 -noverify 参数是因为,不严谨的代码能够运行但是有安全风险,因此才会要加参数来压制。

目前 Hasor 的 字节码增强部分已经 完美运行在各个 jvm 平台之上。

在生成字节码的时候要额外注意下面几项:

  • double、long 两个类型作为入参时候,方法入参上这两种类型的 变量表顺序是要额外 +1 的
  • 不要故意忽略方法变量表的输出 “visitLocalVariable” 虽然可以忽略但是会遇到编译器报错,这个报错可以用过  -noverify 压制,但毕竟是不严谨的。

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!