2024 Clojure 状态调查!分享您的想法。

欢迎!请查阅关于页面以获取更多关于如何使用此信息的信息。

0
Clojure
通过此测试,您可以看到我们无法序列化协议中的方法(即 time-from-tweet),因为这会导致 java.io.NotSerializableException: clojure.lang.MethodImplCache
 at java.io.ObjectOutputStream.writeObject0 (ObjectOutputStream.java:1183)
    java.io.ObjectOutputStream.defaultWriteFields (ObjectOutputStream.java:1547)
    java.io.ObjectOutputStream.writeSerialData (ObjectOutputStream.java:1508)
    java.io.ObjectOutputStream.writeOrdinaryObject (ObjectOutputStream.java:1431)
    java.io.ObjectOutputStream.writeObject0 (ObjectOutputStream.java:1177)
    java.io.ObjectOutputStream.writeObject (ObjectOutputStream.java:347)
    sparkling.protocol_test$serialize.invoke (protocol_test.clj:11)


这是实际的测试

(ns sparkling.protocol-test
  (:require [clojure.test :refer :all])
  (:import [java.io ObjectInputStream ByteArrayInputStream ObjectOutputStream ByteArrayOutputStream]))


(defn- serialize
  "序列化单个对象,返回字节数组。".
  [v]
  (with-open [bout (ByteArrayOutputStream.)
                          oos (ObjectOutputStream. bout)]
                        (.writeObject oos v)
                        (.flush oos)
                        (.toByteArray bout)))

(defn- deserialize
  "从给定的字节数组中反序列化和返回单个对象。"。
  [bytes]
  (with-open [ois (-> bytes ByteArrayInputStream. ObjectInputStream.)]
    (.readObject ois)))


(defprotocol timestamped
  (time-from-tweet [item]))

(defrecord tweet [username tweet timestamp]
  timestamped
  (time-from-tweet [_]
    timestamp
    )))

(deftest sequable-serialization
  (testing "函数序列化"
    (let [item identity]
      (is item (-> item serialize deserialize))))

  (testing "协议方法序列化"
    (let [item time-from-tweet]
      (is item (-> item serialize deserialize))))))

6 个回答

0

评论由:chrisbetz

顺便说一句:多态方法也是如此,这里的异常是java.io.NotSerializableException: clojure.lang.MultiFn

0

评论者:alexmiller

我认为我们不应该期望函数以这种方式进行序列化。协议和多方法实际上有基于实现扩展的运行时状态。对这些函数进行序列化意味着什么?我们会使用在那个点加载的实现进行序列化吗?还是不使用任何实现?这两种情况对我来说似乎都有问题。常规函数是闭包,可以捕获其环境的状态。我认为更好的答案可能是AOT或对于常规函数,类似于serializable-fn库的解决方案。

0

评论由:chrisbetz

你好,

感谢你的评论。首先,关于背景:我正在开发Sparkling,这是一个Clojure API,用于Apache Spark。为了在集群中分发代码,它依赖于AOT编译的函数,所以,是的,你不能简单地序列化任何函数,它需要经过AOT编译。序列化为我们提供了对当前绑定等的支持,一切按预期工作。因此,AFunction之所以可以序列化,有它的原因,AFn/IFn的其他实现也是如此,一切正常。

至于协议和多方法的状态 - 我认为它与函数的状态(哪个函数定义,var可能被绑定多次等)和绑定提供的闭包在概念上相同。用户没有理由相信协议的方法与函数不同。实际上,(ifn? protocol-method) 也返回true。

serializable-fn并非最初就打算用于网络传输的序列化,因此在序列化函数的绑定中的函数集合以及Spark上下文中不断创建同一函数的类导致PermGen污染存在问题。

我认为现在我可以解决问题,因为我可以将协议方法封装在函数中,但我仍然认为这是一个bug。

问候

Chris

0

评论由:chrisbetz

实际上,这是造成痛苦的代码段(链接:https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/AFunction.java 文本:clojure.lang.AFunction)

`
public abstract class AFunction extends AFn implements IObj, Comparator, Fn, Serializable {

public volatile MethodImplCache __methodImplCache;
`

AFunction是可序列化的,但MethodImplCache不可序列化。我不确定是否足够将其标记为transient,因为我没有检查初始化的位置。

0

评论由:chrisbetz

我的邮件评论在SMTP的天堂中丢失了:有一个简单的解决方案。将协议方法包装在一个函数中,这将以使您的代码难看为代价来完成这项工作 ;)

0
参考: https://clojure.atlassian.net/browse/CLJ-1701(由 alex+import 报告)
...