代码之家  ›  专栏  ›  技术社区  ›  Evgeniy Berezovsky

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

  •  2
  • Evgeniy Berezovsky  · 技术社区  · 6 年前

    我有一个clojure项目,使用 lein run 但是 lein uberjar 结果在

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

    对于同一项目。我可以追溯到 some\u文件。clj公司 使用2000多个条目的大向量,定义如下:

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

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

    N、 B.变更时 x 至常数a la (def ^:const x ,则, 莱因跑步 还将抛出 方法太大! 错误顺便说一句,这个错误发生在 十、 习惯于 ,因此,如果不使用常数,只定义它就可以了。

    3 回复  |  直到 6 年前
        1
  •  3
  •   OlegTheCat    6 年前

    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"))
    
        2
  •  0
  •   Evgeniy Berezovsky    6 年前

    事实证明,提前编译是 lein run lein uberjar ,作为我的 project.clj 包含(默认)行

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

    当我移除 :aot :all ,uberjar完成时没有任何抱怨,但也没有aot编译。所以我想我需要关闭aot,或者设法限制它以排除该向量。

    但我希望能够有这个-尽管很大-向量“literal”,并且仍然编译所有内容。但也许将这个巨大的向量分解成一个数据文件,在启动时读取该文件也是一个不错的主意。那我就可以离开 :aot:全部 按原样设置。

        3
  •  0
  •   Arthur Ulfeldt    6 年前

    在您的情况下,是否可以在调用 delay 使其在首次使用时进行计算?

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

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

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

    如果您使用future或memorized函数来获取数据,您将在程序启动时计算数据,并将其存储在内存中,而不是类文件中。

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

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