forked from jruby/jruby
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClassCache.java
More file actions
162 lines (131 loc) · 5.1 KB
/
Copy pathClassCache.java
File metadata and controls
162 lines (131 loc) · 5.1 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package org.jruby.util;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.jruby.RubyInstanceConfig;
/**
* A Simple cache which maintains a collection of classes that can potentially be shared among
* multiple runtimes (or whole JVM).
*/
public class ClassCache<T> {
private static final Logger LOG = LoggerFactory.getLogger("ClassCache");
private final AtomicInteger classLoadCount = new AtomicInteger(0);
private final AtomicInteger classReuseCount = new AtomicInteger(0);
private final ReferenceQueue referenceQueue = new ReferenceQueue();
private final Map<Object, KeyedClassReference> cache =
new ConcurrentHashMap<Object, KeyedClassReference>();
private final ClassLoader classLoader;
private final int max;
/**
* The ClassLoader this class cache will use for any classes generated through it. It is
* assumed that the classloader provided will be a parent loader of any runtime using it.
* @param classLoader to use to generate shared classes
*/
public ClassCache(ClassLoader classLoader, int max) {
assert classLoader != null : "Null classloader provided for ClassCache";
this.classLoader = classLoader;
this.max = max;
}
public ClassCache(ClassLoader classLoader) {
this(classLoader, -1);
}
public interface ClassGenerator {
void generate();
byte[] bytecode();
String name();
}
private static class KeyedClassReference<T> extends WeakReference<Class<T>> {
private final Object key;
public KeyedClassReference(Object key, Class<T> referent, ReferenceQueue<Class<T>> referenceQueue) {
super(referent, referenceQueue);
this.key = key;
}
public Object getKey() {
return key;
}
}
public static class OneShotClassLoader extends ClassLoader implements ClassDefiningClassLoader {
private final static ProtectionDomain DEFAULT_DOMAIN =
JRubyClassLoader.class.getProtectionDomain();
public OneShotClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> defineClass(String name, byte[] bytes) {
return super.defineClass(name, bytes, 0, bytes.length, DEFAULT_DOMAIN);
}
}
public ClassLoader getClassLoader() {
return classLoader;
}
public int getMax() {
return max;
}
public Class<T> cacheClassByKey(Object key, ClassGenerator classGenerator)
throws ClassNotFoundException {
Class<T> contents = null;
if (RubyInstanceConfig.JIT_CACHE_ENABLED) {
WeakReference<Class<T>> weakRef = cache.get(key);
if (weakRef != null) contents = weakRef.get();
if (weakRef == null || contents == null) {
if (isFull()) return null;
contents = defineClass(classGenerator);
cleanup();
cache.put(key, new KeyedClassReference(key, contents, referenceQueue));
} else {
classReuseCount.incrementAndGet();
}
} else {
contents = defineClass(classGenerator);
}
return contents;
}
protected Class<T> defineClass(ClassGenerator classGenerator) {
// attempt to load from classloaders
String className = classGenerator.name();
Class contents = null;
try {
contents = getClassLoader().loadClass(className);
if (RubyInstanceConfig.JIT_LOADING_DEBUG) {
LOG.debug("found jitted code in classloader: {}", className);
}
} catch (ClassNotFoundException cnfe) {
if (RubyInstanceConfig.JIT_LOADING_DEBUG) {
LOG.debug("no jitted code in classloader for method {} at class: {}", classGenerator, className);
}
// proceed to define in-memory
}
OneShotClassLoader oneShotCL = new OneShotClassLoader(getClassLoader());
classGenerator.generate();
contents = oneShotCL.defineClass(classGenerator.name(), classGenerator.bytecode());
classLoadCount.incrementAndGet();
return contents;
}
public void flush() {
cache.clear();
}
public boolean isFull() {
cleanup();
return max > 0 && cache.size() >= max;
}
public int getClassLoadCount() {
return classLoadCount.get();
}
public int getLiveClassCount() {
cleanup();
return cache.size();
}
public int getClassReuseCount() {
return classReuseCount.get();
}
private void cleanup() {
KeyedClassReference reference;
while ((reference = (KeyedClassReference)referenceQueue.poll()) != null) {
cache.remove(reference.getKey());
}
}
}