Android ART 在字节码上运行编译时验证,并因为任何使用锁定宏而失败。错误看起来与 CLJ-1829 中的情况相似(在这种情况下 clojure.core.server/stop-server 调用了锁定宏)
10-16 14:49:26.801 2008-2008/? E/AndroidRuntime: java.lang.VerifyError: 拒绝类 clojure.core.server$stop_server,因为它在编译时验证失败('clojure.core.server$stop_server' 的声明出现在 /data/app/com.clojure_on_android-1/base.apk 中)
原因:根据对 Android 问题的讨论(https://code.google.com/p/android/issues/detail?id=80823),这似乎是因为对 JVM 规范中的“结构锁定”条款执行更严格的执行(https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.11.10)。
似乎在 locking
中的 monitor-enter、monitor-exit 和 try/finally 块的混合创建的路径,ART 正将其标记为没有平衡 monitorenter/monitorexit 字节码。特别是 monitorenter 和 monitorexit 本身可以抛出(在 null 锁定对象上)。Java 字节码对这些情况进行了一些复杂的异常表处理,但这在 Clojure 编译器的修改下是不可能的。
方法:一种可能的方法是让锁定在 Java 中的同步块上下文中调用传递的主体。这避免了通过交给 Java 处理而导致的复杂字节码的问题,但性能影响是未知的。
补丁:clj-1472-3.patch
参见:将字节码与 Java synchronized 块的比较显示了一些差异
https://gist.github.com/AdamClements/2ae6c4919964b71eb470
筛选者:由 Alex Miller 筛选 - 我认为这是一个可行的解决方案,它解决了问题,由于使用频率不高,我认为这不一定是性能问题。我认为另一种处理方法是使 locking
成为具有编译器支持的特设形式,但我不确定这是否值得做,所以我会留给 Rich 决定。