lein uberjar导致“方法代码太大了!”

时间:2018-04-25 02:35:42

标签: clojure leiningen

我有一个使用lein run正常运行的clojure项目,但lein uberjar会导致

java.lang.RuntimeException: Method code too large!, compiling:(some_file.clj:1:1)

为同一个项目。我可以使用超过2000个条目的大向量将其追溯到 some_file.clj ,其定义如下:

(def x
  [[1 :qwre :asdf]
   [2 :zxcv :fafa]
   ...
])

从该向量中删除足够的条目时,lein uberjar编译没有问题。如何让uberjar完成它的工作,将所有条目留在向量中?

N.B。将x更改为常量la (def ^:const x时,lein run也会抛出方法太大!错误。顺便说一下,这个错误发生在x 使用的地方,所以如果没有使用它,只需定义常量即可。

3 个答案:

答案 0 :(得分:3)

Java中方法的大小限制为64kb。在您的情况下,创建大矢量文字的方法似乎超出了此限制。

实际上,您可以使用名为clj-java-decompiler的精美库自行检查。以下是使用boot的简短示例:

(set-env!
 :dependencies '[[com.clojure-goes-fast/clj-java-decompiler "0.1.0"]])

(require '[clj-java-decompiler.core :as d])

(d/decompile-form
 {}
 [[1 :qwre :asdf]
  [2 :zxcv :fafa]
  [3 :zxcv :fafa]])

;; ==> prints:
;;
;; // Decompiling class: cjd__init
;; import clojure.lang.*;
;; 
;; public class cjd__init
;; {
;;  public static final AFn const__10;
;;  
;;  public static void load() {
;;                             const__10;
;;                             }
;;  
;;  public static void __init0() {
;;                                const__10 = (AFn)Tuple.create((Object)Tuple.create((Object)1L, (Object)RT.keyword((String)null, "qwre"), (Object)RT.keyword((String)null, "asdf")), (Object)Tuple.create((Object)2L, (Object)RT.keyword((String)null, "zxcv"), (Object)RT.keyword((String)null, "fafa")), (Object)Tuple.create((Object)3L, (Object)RT.keyword((String)null, "zxcv"), (Object)RT.keyword((String)null, "fafa")));
;;                                }
;;  
;;  static {
;;          __init0();
;;          Compiler.pushNSandLoader(RT.classForName("cjd__init").getClassLoader());
;;          try {
;;               load();
;;               Var.popThreadBindings();
;;               }
;;          finally {
;;                   Var.popThreadBindings();
;;                   }
;;          }
;;  }
;; 

如您所见,有一个方法__init0可以为矢量文字创建实际的Java对象。如果向量中有足够的元素,方法的大小很容易超过64kb的限制。

我认为,在您的情况下,解决此问题的最简单方法是将矢量放到文件中,然后slurp +读取此文件。像这样的东西:

档案 vector.edn

[[1 :qwre :asdf]
 [2 :zxcv :fafa]
 ...
 ]

然后在你的代码中:

(def x (clojure.edn/read-string (slurp "vector.edn"))

答案 1 :(得分:0)

在你的情况下是否可以在delay的调用中将值包装起来,以便在第一次使用它时计算它?

对java类文件中的方法有多大限制,而Clojure中的每个顶级表单(通常)都会生成一个类。

要解决这个问题,安排生成和存储常量是很有用的:

  • 通过在运行时计算它们在内存中
  • 在资源目录中,可以在运行时读取它们
  • 不通过事先编译来生成类文件,这会在程序启动时生成数据。

如果您使用未来或创建一个memoized函数来获取数据,您将在程序启动时计算它并将其存储在内存而不是类文件中。

如果你把它放在资源目录中,它将不受大小限制的限制,并且仍然可以在编译时计算。

如果禁用AOT编译,那么类限制将永远不会被命中,因为它将在程序启动时的加载时计算。

答案 2 :(得分:0)

事实证明,提前编译是lein runlein uberjar之间的差异,因为我的project.clj包含(默认)行

:profiles {:uberjar {:aot :all}})

当我删除:aot :all时,uberjar在没有抱怨的情况下完成 - 但也没有进行任何编译。所以我想我需要关闭aot,或者想出限制它来排除那个向量。

但是我希望能够拥有这个 - 尽管很大 - 矢量"字面值",并且仍然可以编译所有内容。但也许将这个巨大的矢量分解成一个数据文件,在启动时读入也不是一个好主意。然后,我就可以保留:aot :all设置。