调用`(get-thread-binding)`会导致堆栈溢出

时间:2017-06-12 22:38:52

标签: clojure

当我跑步时

  

使用者> (获得线程绑定)

我看到了

  

StackOverflowError clojure.lang.PersistentHashMap $ BitmapIndexedNode.index(PersistentHashMap.java:677)

这是一个新的lein new foo项目,我有一行

  

(def foo“Hello World”)

堆栈跟踪重复调用clojure.core/pr-onclojure.core/print-map等,但触发此操作的初始调用不可见。

1 个答案:

答案 0 :(得分:3)

每当您在REPL中计算表达式时,三个最新表达式的结果都存储在名为*1*2*3的特殊线程绑定变量中。这些变量包含在get-thread-bindings返回的映射中,因为它们是线程绑定的。特别是,*1是所有线程绑定变量的映射,其值之一是var *1

在内存中存在这样的循环引用是可以的,但是当你尝试打印它到REPL时,你会遇到麻烦,因为它看起来像一棵无限深的树。相反,如果你想环顾(get-thread-bindings)返回的地图,你必须要小心一点,只看它不是无限的部分。例如,我在这里仔细检查我的问题答案是否正确:

user=> (class (get-thread-bindings))
clojure.lang.PersistentHashMap
user=> (keys (get-thread-bindings))
(#<Var: --unnamed--> #<Var: --unnamed--> #'clojure.core/*assert* #'clojure.core/*compile-path* #'clojure.core/*math-context* #'clojure.test/*test-out* #'clojure.core/*out* #<Var: --unnamed--> #'clojure.core/*2 #'clojure.core/*source-path* #'clojure.core/*err* #'clojure.core/*data-readers* #<Var: --unnamed--> #'clojure.core/*command-line-args* #<Var: --unnamed--> #'clojure.core/*warn-on-reflection* #'clojure.tools.nrepl.middleware.interruptible-eval/*msg* #'clojure.core/*read-eval* #'clojure.core/*default-data-reader-fn* #'clojure.core/*1 #'clojure.core/*unchecked-math* #'clojure.core/*e #'clojure.core/*file* #'clojure.core/*print-length* #'clojure.core/*3 #<Var: --unnamed--> #<Var: --unnamed--> #<Var: --unnamed--> #'clojure.core/*ns* #'clojure.core/*print-level* #<Var: --unnamed--> #<Var: --unnamed--> #<Var: --unnamed--> #'clojure.core/*in* #'clojure.core/*print-meta* #'clojure.tools.nrepl.middleware.session/*out-limit*)
user=> (map (comp :name meta) (keys (get-thread-bindings)))
(nil nil *assert* *compile-path* *math-context* *test-out* *out* nil *2 *source-path* *err* *data-readers* nil *command-line-args* nil *warn-on-reflection* *msg* *read-eval* *default-data-reader-fn* *1 *unchecked-math* *e *file* *print-length* *3 nil nil nil *ns* *print-level* nil nil nil *in* *print-meta* *out-limit*)

圆形结构在像Clojure这样的面向数据的语言中有点痛苦,因为你已经习惯于打印任何东西以查看它的全部内容,而且你可以&#39;打印这些。但是你仍然可以通过查看它们是什么类来调查它们,然后以某种方式调查它们(例如,对于地图,查看它的键通常是一个好主意)。 / p>

假设Stack Overflow不存在,或者您被困在没有Internet访问权限的空间站上,您怎么能自己发现*1现象?一旦你看到打印所有get-thread-bindings是不可能的,你就可以发现它是一张地图,然后看看它的按键,看看当你打印它们时哪些会爆炸,哪些不是?吨。然后你可以进一步研究爆炸性的(*1),并发现它是另一个地图,其中包含另一个名为*1的键,其值是另一个地图...希望这会让你有所了解如何在将来调试类似的问题。