class loaders stack constant grow in REPL

157 views
Skip to first unread message

Vladimir Tsichevski

unread,
Dec 10, 2012, 3:46:10 AM12/10/12
to clo...@googlegroups.com
Hi,

just found that every interaction with Clojure REPL causes one more DynamicClassLoader put on the Thread context class loader chain.

Here is how clojure.main/repl beginning looks like:

  (let [cl (.getContextClassLoader (Thread/currentThread))]
    (.setContextClassLoader (Thread/currentThread) (clojure.lang.DynamicClassLoader. cl)))

And this is how to observe it:

nREPL server started on port 19987
REPL-y 0.1.0-beta10
Clojure 1.4.0
    Exit: Control+D or (exit) or (quit)
Commands: (user/help)
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
          (user/sourcery function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
Examples from clojuredocs.org: [clojuredocs or cdoc]
          (user/clojuredocs name-here)
          (user/clojuredocs "ns-here" "name-here")
user=> (defn print-class-loader-stack
  ([]
     (print-class-loader-stack (.getContextClassLoader (Thread/currentThread))))
  ([cl]
     (if cl
       (do
         (pprint cl)
         (print-class-loader-stack (.getParent cl)))
       (println "*Top*"))))
  #_=>   #_=>   #_=>   #_=>   #_=>   #_=>   #_=>   #_=> #'user/print-class-loader-stack
user=> (print-class-loader-stack)
#<DynamicClassLoader clojure.lang.DynamicClassLoader@26a0c73f>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@6f603bdc>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@2f368c5d>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@5b31fd9>
#<AppClassLoader sun.misc.Launcher$AppClassLoader@4aad3ba4>
#<ExtClassLoader sun.misc.Launcher$ExtClassLoader@3326b249>
*Top*
nil
user=> 1
1
user=> 1
1
user=> 1
1
user=> 1
1
user=> (print-class-loader-stack)
#<DynamicClassLoader clojure.lang.DynamicClassLoader@5be04861>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@7481933a>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@273f212a>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@4178feba>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@5323961b>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@26a0c73f>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@6f603bdc>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@2f368c5d>
#<DynamicClassLoader clojure.lang.DynamicClassLoader@5b31fd9>
#<AppClassLoader sun.misc.Launcher$AppClassLoader@4aad3ba4>
#<ExtClassLoader sun.misc.Launcher$ExtClassLoader@3326b249>
*Top*
nil
user=> 

Colin Jones

unread,
Dec 10, 2012, 11:32:36 AM12/10/12
to clo...@googlegroups.com
Right, this is because nREPL uses clojure.main/repl each time it does an evaluation. See http://dev.clojure.org/jira/browse/NREPL-31 for a related issue that was addressed by modifying clojure.main/repl.

I'm not sure where a fix for this would belong (nREPL or clojure.main), but I went ahead and opened an nREPL JIRA issue to track it: http://dev.clojure.org/jira/browse/NREPL-36

- Colin

Vladimir Tsichevski

unread,
Dec 10, 2012, 2:15:32 PM12/10/12
to clo...@googlegroups.com
Thank you Colin,

I think, the main problem is nobody has ever tried to write an article "Class loading in Clojure". If such article existed, it would make life much easier for many developers.

Regards,
Vladimir

Tassilo Horn

unread,
Dec 10, 2012, 2:40:58 PM12/10/12
to Colin Jones, clo...@googlegroups.com
Colin Jones <trpt...@gmail.com> writes:

Hi Colin,

> Right, this is because nREPL uses clojure.main/repl each time it does
> an evaluation. See http://dev.clojure.org/jira/browse/NREPL-31 for a
> related issue that was addressed by modifying clojure.main/repl.
>
> I'm not sure where a fix for this would belong (nREPL or
> clojure.main), but I went ahead and opened an nREPL JIRA issue to
> track it: http://dev.clojure.org/jira/browse/NREPL-36

Some time ago, I was also astonished by the large number of class
loaders. But I'm pretty sure that the class loader list (i.e.,
ClassLoader.parent -> ClassLoader.parent -> ...) didn't grow without
bounds, but sometimes a parent becomes set to null and the previously
referenced class loader and all its parents becomes garbage collected.

I didn't do any debugging, but just used a function similar to
Vladimir's `print-class-loader-stack` every once in a while.

Bye,
Tassilo

Chas Emerick

unread,
Dec 10, 2012, 3:11:42 PM12/10/12
to clo...@googlegroups.com
Good catch, guys.

Interesting that I never noticed this; likely because Pomegranate's `classloader-hierarchy` function ends up starting its walk from (RT/baseLoader) (which will be changing to the context classloader shortly):


I'm open to suggestions.  I'm not keen on having yet another divergence in nREPL behaviour between 1.5.0 and all prior revs of Clojure, so an nREPL-local fix would be great.

My first impulse right now is to boot clojure.main/repl entirely and groom a replacement "in-house", as it were (which I probably should have done from day 1).

- Chas

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply all
Reply to author
Forward
0 new messages