-
Notifications
You must be signed in to change notification settings - Fork 0
/
gc.clj
130 lines (101 loc) · 4.56 KB
/
gc.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
(ns tech.v3.resource.gc
"System for using both weak and soft references generically. Weak references don't
count for anything in the gc while soft references will keep their object alive as
long as there is no gc pressure."
(:require [tech.v3.resource.stack :as stack]
[clojure.tools.logging :as log])
(:import [java.lang.ref ReferenceQueue]
[java.lang Thread]
[tech.resource GCReference GCSoftReference]
[java.util.concurrent ConcurrentHashMap]
[java.util Set]))
(set! *warn-on-reflection* true)
(def ^ReferenceQueue reference-queue (ReferenceQueue.))
(def ^Set weak-reference-set (ConcurrentHashMap/newKeySet))
(defn watch-reference-queue
[run-atom ^ReferenceQueue reference-queue]
(try
(log/info "Reference thread starting")
(loop [continue? @run-atom]
(when continue?
(let [next-ref (.remove reference-queue 100)]
(when next-ref
(try
(stack/do-release next-ref)
;;We can't let a bad thing kill the thread. Do release already prints
;;diagnostic information so that is sufficient for now.
(catch Throwable _ nil)))
(recur @run-atom))))
(catch Throwable e
(log/errorf e "!!Error in reference queue!!")))
(log/info "Reference queue exiting"))
(defonce reference-thread* (atom nil))
(defn start-reference-thread
[]
(when-not @reference-thread*
(let [run-atom (atom true)
thread (Thread. #(watch-reference-queue run-atom reference-queue))]
;;Do not stop the jvm from exiting...
(.setDaemon thread true)
(.setName thread "tech.resource.gc ref thread")
(.start thread)
(reset! reference-thread*
{:thread thread
:close-fn #(do
(reset! run-atom false)
(.join thread))}))))
(defn stop-reference-thread
[]
(when-let [close-fn (:close-fn @reference-thread*)]
(close-fn)
(reset! reference-thread* nil)))
(def reference-thread (delay (start-reference-thread)))
(defn- create-reference
[item dispose-fn ptr-constructor]
;;ensure the cleanup thread is running.
@reference-thread
(let [retval (ptr-constructor item reference-queue
(fn [this-ref]
(.remove weak-reference-set this-ref)
(dispose-fn)))]
(.add weak-reference-set retval)
retval))
(defn gc-reference
"Create a weak reference to the item. Return the actual reference. You can get the
item (assuming it hasn't been cleaned up) using .get method on the reference. Note
that dispose-fn must *not* reference item in any way else the item won't get cleaned
up.
IF track-reference is *true*, then the reference itself is added to the reference set.
This keeps the reference itself from being gc'd. This is not necessary if you know
the reference will outlive the tracked object (or if you don't care).
**Note** - Under situation of high garbage collector activity the caller *must* hold
a reference to item else it could get disposed of *during* this function call.
"
^GCReference [item dispose-fn]
(create-reference item dispose-fn #(GCReference. %1 %2 %3)))
(defn soft-reference
"Create a soft reference to the item. Return the actual reference. You can get the
item (assuming it hasn't been cleaned up) using .get method on the reference. Note
that dispose-fn must *not* reference item in any way else the item won't get cleaned
up.
If track-reference is *true*, then the reference itself is added to the reference set.
This keeps the reference itself from being gc'd. This is not necessary if you know
the reference will outlive the tracked object (or if you don't care).
**Note** - Under situation of high garbage collector activity the caller *must* hold
a reference to item else it could get disposed of *during* this function call."
^GCSoftReference [item dispose-fn]
(create-reference item dispose-fn #(GCSoftReference. %1 %2 %3)))
(defn track-gc-only
"Track this item using weak references. Note that the dispose-fn must absolutely
*not* reference the item else nothing will ever get released."
[item dispose-fn]
(gc-reference item dispose-fn)
item)
(defn track
"Track an item via both the gc system *and* the stack based system. Dispose will be
first-one-wins. Dispose-fn must not reference item else the circular dependency will
stop the dispose-fn from being called."
[item dispose-fn]
(-> (gc-reference item dispose-fn)
(stack/track))
item)