Skip to content

Commit 729bf70

Browse files
committed
support ref part 1
1 parent 645c5c8 commit 729bf70

File tree

10 files changed

+498
-2
lines changed

10 files changed

+498
-2
lines changed

core/src/main/java/com/taobao/arthas/core/command/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public interface Constants {
1919
" throwExp : the throw exception of method\n" +
2020
" isReturn : the method ended by return\n" +
2121
" isThrow : the method ended by throwing exception\n" +
22+
" #ref : global object reference store (weak reference)\n" +
2223
" #cost : the execution time in ms of method invocation";
2324

2425
String EXAMPLE = "\nEXAMPLES:\n";

core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ public static Express threadLocalExpress(Object object) {
3030
express = new OgnlExpress();
3131
expressRef.set(new WeakReference<Express>(express));
3232
}
33-
return express.reset().bind(object);
33+
return express.reset().bind(object).bind("ref", ObjectRefStore.ref());
3434
}
3535

3636
public static Express unpooledExpress(ClassLoader classloader) {
3737
if (classloader == null) {
3838
classloader = ClassLoader.getSystemClassLoader();
3939
}
40-
return new OgnlExpress(new ClassLoaderClassResolver(classloader));
40+
return new OgnlExpress(new ClassLoaderClassResolver(classloader)).bind("ref", ObjectRefStore.ref());
4141
}
4242
}
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
package com.taobao.arthas.core.command.express;
2+
3+
import java.lang.ref.Reference;
4+
import java.lang.ref.ReferenceQueue;
5+
import java.lang.ref.WeakReference;
6+
import java.util.ArrayList;
7+
import java.util.Collections;
8+
import java.util.Iterator;
9+
import java.util.LinkedHashMap;
10+
import java.util.LinkedHashSet;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.Set;
14+
import java.util.concurrent.atomic.AtomicLong;
15+
16+
public final class ObjectRefStore {
17+
private static final int DEFAULT_MAX_ENTRIES = 1024;
18+
private static final String DEFAULT_NAMESPACE = "default";
19+
20+
private static final ObjectRefStore INSTANCE = new ObjectRefStore(DEFAULT_MAX_ENTRIES);
21+
22+
private final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
23+
private final LinkedHashMap<StoreKey, Entry> entries;
24+
private final int maxEntries;
25+
private final AtomicLong idGenerator = new AtomicLong(0);
26+
27+
private final Ref rootRef;
28+
29+
private ObjectRefStore(int maxEntries) {
30+
this.maxEntries = maxEntries;
31+
this.entries = new LinkedHashMap<StoreKey, Entry>(16, 0.75f, true);
32+
this.rootRef = new Ref(this, DEFAULT_NAMESPACE);
33+
}
34+
35+
public static Ref ref() {
36+
return INSTANCE.rootRef;
37+
}
38+
39+
private synchronized void drainReferenceQueue() {
40+
Reference<? extends Object> reference;
41+
while ((reference = referenceQueue.poll()) != null) {
42+
if (!(reference instanceof KeyedWeakReference)) {
43+
continue;
44+
}
45+
KeyedWeakReference keyedReference = (KeyedWeakReference) reference;
46+
Entry current = entries.get(keyedReference.key);
47+
if (current != null && current.id == keyedReference.id) {
48+
entries.remove(keyedReference.key);
49+
}
50+
}
51+
}
52+
53+
private static String normalizeNamespace(String namespace) {
54+
if (namespace == null) {
55+
return DEFAULT_NAMESPACE;
56+
}
57+
String trimmed = namespace.trim();
58+
if (trimmed.isEmpty()) {
59+
return DEFAULT_NAMESPACE;
60+
}
61+
return trimmed;
62+
}
63+
64+
private static String requireKey(String key) {
65+
if (key == null) {
66+
throw new IllegalArgumentException("key is null");
67+
}
68+
String trimmed = key.trim();
69+
if (trimmed.isEmpty()) {
70+
throw new IllegalArgumentException("key is blank");
71+
}
72+
return trimmed;
73+
}
74+
75+
private synchronized Object put(String namespace, String key, Object value) {
76+
drainReferenceQueue();
77+
78+
String normalizedNamespace = normalizeNamespace(namespace);
79+
String normalizedKey = requireKey(key);
80+
81+
if (value == null) {
82+
remove(normalizedNamespace, normalizedKey);
83+
return null;
84+
}
85+
86+
long now = System.currentTimeMillis();
87+
StoreKey storeKey = new StoreKey(normalizedNamespace, normalizedKey);
88+
long id = idGenerator.incrementAndGet();
89+
Entry entry = Entry.create(id, storeKey, value, now, referenceQueue);
90+
entries.put(storeKey, entry);
91+
evictIfNecessary();
92+
return value;
93+
}
94+
95+
private synchronized Object get(String namespace, String key) {
96+
drainReferenceQueue();
97+
98+
String normalizedNamespace = normalizeNamespace(namespace);
99+
String normalizedKey = requireKey(key);
100+
101+
StoreKey storeKey = new StoreKey(normalizedNamespace, normalizedKey);
102+
Entry entry = entries.get(storeKey);
103+
if (entry == null) {
104+
return null;
105+
}
106+
107+
Object value = entry.reference.get();
108+
if (value == null) {
109+
entries.remove(storeKey);
110+
return null;
111+
}
112+
entry.lastAccessTime = System.currentTimeMillis();
113+
return value;
114+
}
115+
116+
private synchronized Object remove(String namespace, String key) {
117+
drainReferenceQueue();
118+
119+
String normalizedNamespace = normalizeNamespace(namespace);
120+
String normalizedKey = requireKey(key);
121+
122+
StoreKey storeKey = new StoreKey(normalizedNamespace, normalizedKey);
123+
Entry entry = entries.remove(storeKey);
124+
if (entry == null) {
125+
return null;
126+
}
127+
return entry.reference.get();
128+
}
129+
130+
private synchronized void clearNamespace(String namespace) {
131+
drainReferenceQueue();
132+
133+
String normalizedNamespace = normalizeNamespace(namespace);
134+
Iterator<Map.Entry<StoreKey, Entry>> it = entries.entrySet().iterator();
135+
while (it.hasNext()) {
136+
Map.Entry<StoreKey, Entry> mapEntry = it.next();
137+
if (normalizedNamespace.equals(mapEntry.getKey().namespace)) {
138+
it.remove();
139+
}
140+
}
141+
}
142+
143+
private synchronized void clearAll() {
144+
drainReferenceQueue();
145+
entries.clear();
146+
}
147+
148+
private synchronized List<Map<String, Object>> list(String namespace) {
149+
drainReferenceQueue();
150+
151+
String normalizedNamespace = normalizeNamespace(namespace);
152+
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
153+
154+
long now = System.currentTimeMillis();
155+
Iterator<Map.Entry<StoreKey, Entry>> it = entries.entrySet().iterator();
156+
while (it.hasNext()) {
157+
Map.Entry<StoreKey, Entry> mapEntry = it.next();
158+
StoreKey storeKey = mapEntry.getKey();
159+
if (!normalizedNamespace.equals(storeKey.namespace)) {
160+
continue;
161+
}
162+
163+
Entry entry = mapEntry.getValue();
164+
Object value = entry.reference.get();
165+
if (value == null) {
166+
it.remove();
167+
continue;
168+
}
169+
170+
Map<String, Object> item = new LinkedHashMap<String, Object>();
171+
item.put("namespace", storeKey.namespace);
172+
item.put("name", storeKey.key);
173+
item.put("class", entry.className);
174+
item.put("identityHash", entry.identityHashHex);
175+
item.put("classLoader", entry.classLoader);
176+
item.put("createTime", entry.createTime);
177+
item.put("lastAccessTime", entry.lastAccessTime);
178+
item.put("ageMillis", now - entry.createTime);
179+
item.put("idleMillis", now - entry.lastAccessTime);
180+
result.add(item);
181+
}
182+
183+
return result;
184+
}
185+
186+
private synchronized List<String> namespaces() {
187+
drainReferenceQueue();
188+
189+
Set<String> namespaces = new LinkedHashSet<String>();
190+
Iterator<Map.Entry<StoreKey, Entry>> it = entries.entrySet().iterator();
191+
while (it.hasNext()) {
192+
Map.Entry<StoreKey, Entry> mapEntry = it.next();
193+
Entry entry = mapEntry.getValue();
194+
if (entry.reference.get() == null) {
195+
it.remove();
196+
continue;
197+
}
198+
namespaces.add(mapEntry.getKey().namespace);
199+
}
200+
List<String> result = new ArrayList<String>(namespaces);
201+
Collections.sort(result);
202+
return result;
203+
}
204+
205+
private void evictIfNecessary() {
206+
while (entries.size() > maxEntries) {
207+
Iterator<Map.Entry<StoreKey, Entry>> it = entries.entrySet().iterator();
208+
if (!it.hasNext()) {
209+
return;
210+
}
211+
it.next();
212+
it.remove();
213+
}
214+
}
215+
216+
public static final class Ref {
217+
private final ObjectRefStore store;
218+
private final String namespace;
219+
220+
private Ref(ObjectRefStore store, String namespace) {
221+
this.store = store;
222+
this.namespace = namespace;
223+
}
224+
225+
public Ref ns(String namespace) {
226+
return new Ref(store, normalizeNamespace(namespace));
227+
}
228+
229+
public String namespace() {
230+
return namespace;
231+
}
232+
233+
public Object put(String key, Object value) {
234+
return store.put(namespace, key, value);
235+
}
236+
237+
public Object get(String key) {
238+
return store.get(namespace, key);
239+
}
240+
241+
public Object remove(String key) {
242+
return store.remove(namespace, key);
243+
}
244+
245+
public void clear() {
246+
store.clearNamespace(namespace);
247+
}
248+
249+
public void clearAll() {
250+
store.clearAll();
251+
}
252+
253+
public List<Map<String, Object>> ls() {
254+
return store.list(namespace);
255+
}
256+
257+
public List<String> namespaces() {
258+
return store.namespaces();
259+
}
260+
}
261+
262+
private static final class StoreKey {
263+
private final String namespace;
264+
private final String key;
265+
private final int hash;
266+
267+
private StoreKey(String namespace, String key) {
268+
this.namespace = namespace;
269+
this.key = key;
270+
this.hash = 31 * namespace.hashCode() + key.hashCode();
271+
}
272+
273+
@Override
274+
public boolean equals(Object obj) {
275+
if (this == obj) {
276+
return true;
277+
}
278+
if (!(obj instanceof StoreKey)) {
279+
return false;
280+
}
281+
StoreKey other = (StoreKey) obj;
282+
return namespace.equals(other.namespace) && key.equals(other.key);
283+
}
284+
285+
@Override
286+
public int hashCode() {
287+
return hash;
288+
}
289+
}
290+
291+
private static final class Entry {
292+
private final long id;
293+
private final KeyedWeakReference reference;
294+
private final String className;
295+
private final String classLoader;
296+
private final String identityHashHex;
297+
private final long createTime;
298+
private volatile long lastAccessTime;
299+
300+
private Entry(long id, KeyedWeakReference reference, String className, String classLoader, String identityHashHex,
301+
long createTime, long lastAccessTime) {
302+
this.id = id;
303+
this.reference = reference;
304+
this.className = className;
305+
this.classLoader = classLoader;
306+
this.identityHashHex = identityHashHex;
307+
this.createTime = createTime;
308+
this.lastAccessTime = lastAccessTime;
309+
}
310+
311+
private static Entry create(long id, StoreKey key, Object value, long now, ReferenceQueue<Object> queue) {
312+
Class<?> clazz = value.getClass();
313+
String className = clazz.getName();
314+
315+
ClassLoader loader = clazz.getClassLoader();
316+
String classLoader = loader == null ? "bootstrap"
317+
: loader.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(loader));
318+
319+
String identityHashHex = Integer.toHexString(System.identityHashCode(value));
320+
KeyedWeakReference reference = new KeyedWeakReference(value, queue, key, id);
321+
return new Entry(id, reference, className, classLoader, identityHashHex, now, now);
322+
}
323+
}
324+
325+
private static final class KeyedWeakReference extends WeakReference<Object> {
326+
private final StoreKey key;
327+
private final long id;
328+
329+
private KeyedWeakReference(Object referent, ReferenceQueue<Object> q, StoreKey key, long id) {
330+
super(referent, q);
331+
this.key = key;
332+
this.id = id;
333+
}
334+
}
335+
}
336+

0 commit comments

Comments
 (0)