Clojure lein uberjar的结果是;方法代码太大&引用;

Clojure lein uberjar的结果是;方法代码太大&引用;,clojure,leiningen,Clojure,Leiningen,我有一个clojure项目,它使用leinrun运行良好,但是leinuberjar会导致 java.lang.RuntimeException: Method code too large!, compiling:(some_file.clj:1:1) 对于同一个项目。我可以使用一个包含2000多个条目的大向量将其追溯到某个_file.clj,该向量的定义如下: (def x [[1 :qwre :asdf] [2 :zxcv :fafa] ... ]) 当从该向量中删除足

我有一个clojure项目,它使用
leinrun
运行良好,但是
leinuberjar
会导致

java.lang.RuntimeException: Method code too large!, compiling:(some_file.clj:1:1)
对于同一个项目。我可以使用一个包含2000多个条目的大向量将其追溯到某个_file.clj,该向量的定义如下:

(def x
  [[1 :qwre :asdf]
   [2 :zxcv :fafa]
   ...
])
当从该向量中删除足够的条目时,
leinuberjar
编译时不会出现问题。我如何让uberjar完成它的工作,将所有条目保留在向量中


注意:将
x
更改为常数a la
(def^:const x
lein run
也会抛出一个太大的方法!错误。顺便说一句,该错误发生在使用
x
的地方,因此只要定义一个常量就可以了,如果它没有被使用。

在您的情况下,在调用
delay
时将该值顶部包装起来,以便在第一次调用时计算它用过吗

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

要解决此问题,最好安排生成和存储常量:

  • 通过在运行时计算它们在内存中
  • 在可在运行时读取的资源目录中
  • 不要通过不预先编译来生成类文件,这会在程序启动时生成数据
如果您使用future或memorized函数来获取数据,您将在程序启动时计算数据,并将其存储在内存中,而不是类文件中

如果您将它放在resources目录中,它将不受大小限制,并且仍然可以在编译时进行计算


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

结果表明,提前编译是
lein run
lein uberjar
之间的区别,因为my
project.clj
包含(默认)行

当我删除
:aot:all
时,uberjar毫无怨言地完成了—但也没有aot编译。因此我想我需要关闭aot,或者设法限制它以排除该向量


但我希望能够有这个——尽管很大——向量“文字”,并且仍然编译所有内容。但是,也许将这个巨大的向量分解成一个数据文件,在启动时读入也是一个不错的主意。然后我就可以保持
:aot:all
设置不变。

在Java中,该方法的大小有64kb的限制。在您的例子中,似乎是创建大向量的方法文字超过此限制

实际上,您可以使用一个名为
cljjava反编译程序
的奇特库自己检查它

(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();
;;                   }
;;          }
;;  }
;; 
如您所见,有一个方法
\uu init0
为向量文本创建实际的Java对象。如果向量中有足够的元素,该方法的大小很容易超过64kb的限制

我认为,在您的情况下,解决此问题的最简单方法是将向量放入文件,然后
slurp
+读取此文件。例如:

(def x
  [[1 :qwre :asdf]
   [2 :zxcv :fafa]
   ...
])
文件
vector.edn

[[1 :qwre :asdf]
 [2 :zxcv :fafa]
 ...
 ]
然后在代码中:

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

我试图将其更改为
(def x-delay(delay[…])(def x@c-delay)
(这样我就不必更改使用x的代码,它散布在代码库中,但我仍然得到一个“方法代码太大了!”!“错误。在顶层解除对已达成协议的未来的依赖将相当于毫不拖延地在顶层定义它。我将扩展答案以进一步讨论这一点。这正是我所担心的。最后,我现在正在吞咽一个edn资源,正如OlegTheCat所建议的那样。是的,这就是我现在正在做的:吞咽一个edn资源文件。