字节码
我们经常听到字节码,简单的说字节码就是Java源代码到虚拟机执行的期间生成的中间代码。
JVM
虽然叫Java虚拟机,但是更准确的叫法我觉得应该是字节码虚拟机。
因为我们听过很多的JVM语言,其实它们之所以能在JVM上运行,也是编译成字节码丢进去运行的。
我们一般使用javac
来生成java字节码来让jvm去运行。
那其实宽泛的说javac
也就是一个字节码生成器。
因为字节码是二进制流,所以我们自然也可以用java
语言手动产生一个字节码流。
字节码生成器
字节码生成器说到底就是根据字节码规范去用程序生成字节码。
一般用的比较多的就是Javaassist
,CGLIB
之类。
提到CGLIB
我们很容易想到的就是代理。
不过在java的动态代理的实现中,也是利用这种方法自动产生动态类加载进去的。
动态代理
1 | //声明一个接口 |
运行结果是
beforeSayHello
hello
beforeSayWorld
world
那么大概动态代理帮我们生成了什么代理类呢
声明大概是这样
1 | public class ProxyClass extends Students implements Greeting { |
并且把其中的每个方法的内容大概都改为了
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
的内容。
Proxy类
1 | public static Object newProxyInstance(ClassLoader loader, |
进行了一些缩减,try里面的代码是对新的类进行了newInstance,所以在getProxyClass0中就已经生成好了新的代理类并进行了加载。
1 | private static Class<?> getProxyClass0(ClassLoader loader, |
这里的proxyClassCache,在类中定义为,保存的是代理Class的缓存
1 | private static final WeakCache<ClassLoader, Class<?>[], Class<?>> |
简单说一下这个WeakCache的作用,在这儿之前,先看看new的两个类分别是做什么用的?
先看KeyFactory1
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/*
* 这个类是一个函数式类,实现了BiFunction,实现了apply方法
* 其中Key1,Key2和KeyX类都是大概是下面这样,选取了KeyX
* private final int hash;
* private final WeakReference<Class<?>>[] refs;
* KeyX(Class<?>[] interfaces) {
* hash = Arrays.hashCode(interfaces);
* refs = (WeakReference<Class<?>>[])new WeakReference<?>[interfaces.length];
* for (int i = 0; i < interfaces.length; i++) {
* refs[i] = new WeakReference<>(interfaces[i]);
* }
* 就是说把构造参数整体取个Hash存在hash属性中,然后把每个interface都放到一个弱引用中
* 如果你知道java中的引用级别的话,就知道弱引用的对象只能活到下次gc前。
* 其中key0是这么写的
* private static final Object key0 = new Object();
*
*/
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
再来看看ProxyClassFactory1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
*
* 这个类也是实现了一个函数式接口,实现了apply方法,最终的结果就是返回了我们要生成的代理类
*
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//返回代理类的class
}
}
看完了参数中的两个类,我们再看看看WeakCache到底怎么实现缓存的,因为直观上看,其实我们觉得这个缓存类非常复杂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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217/**
* 我们还是具体问题具体分析,根据上面的构造
* WeakCache<ClassLoader, Class<?>[], Class<?>>
* K是ClassLoader, P是接口数组,V是要产生的代理类
* 还是回到最原始的问题上来,既然我们知道这个是做缓存用的,那么最基本的肯定要是Map的key,value的格式
* 根据这个最基本的点,我们来看是怎么做的
*/
final class WeakCache<K, P, V> {
/**
* 这个refQueue是用来保存
* 在weak reference指向的对象被回收后, weak reference本身其实也就没有用了.
* java提供了一个ReferenceQueue来保存这些所指向的对象已经被回收的reference.
* 用法是在定义WeakReference的时候将一个ReferenceQueue的对象作为参数传入构函数.
*/
private final ReferenceQueue<K> refQueue
= new ReferenceQueue<>();
//这里是实际的缓存类,这里配备的是两级缓存
//第一级就是对应的classLoader缓存
//第二级是subKeyFactory.apply(key, parameter)产生的key
//supplier#get返回的就是我们需要的代理类class
//为什么需要按照classLoader来划分呢,因为双亲委派机制的存在
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
= new ConcurrentHashMap<>();
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
= new ConcurrentHashMap<>();
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;
//构造函数,我们传进去了
//new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
//如果你还记得,KeyFactory里面有个字段是对所有的接口做了hash
//ProxyClassFactory是返回我们要生成的代理类
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
/**
* WeakCache中还有几个静态内部类,CacheValue和CacheKey
*/
/**
* Value继承了Supplier接口,Supplier也是一个函数式接口,里面有一个get方法
* public interface Supplier<T> {
* T get();
* }
* CacheValue继承了WeakReference类,也就是弱引用。
* 其中的hash值调用了System.identityHashCode
* 这是个native方法,还是返回了hashcode,那为什么不直接调用hashcode方法呢,
* 文档中写的是这个方法排除了这个类覆盖了hashcode方法,也就是调用了Object的hashcode方法
*/
private interface Value<V> extends Supplier<V> {}
private static final class CacheValue<V>
extends WeakReference<V> implements Value<V>
{
private final int hash;
CacheValue(V value) {
super(value);
this.hash = System.identityHashCode(value);
}
public int hashCode() {
return hash;
}
public boolean equals(Object obj) {
V value;
return obj == this ||
obj instanceof Value &&
// cleared CacheValue is only equal to itself
(value = get()) != null &&
value == ((Value<?>) obj).get(); // compare by identity
}
}
/**
* CacheKey也是继承了WeakReference方法
*/
private static final class CacheKey<K> extends WeakReference<K> {
// a replacement for null keys
private static final Object NULL_KEY = new Object();
static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) {
return key == null
// null key means we can't weakly reference it,
// so we use a NULL_KEY singleton as cache key
? NULL_KEY
// non-null key requires wrapping with a WeakReference
: new CacheKey<>(key, refQueue);
}
private final int hash;
private CacheKey(K key, ReferenceQueue<K> refQueue) {
super(key, refQueue);
this.hash = System.identityHashCode(key); // compare by identity
}
public int hashCode() {
return hash;
}
public boolean equals(Object obj) {
K key;
return obj == this ||
obj != null &&
obj.getClass() == this.getClass() &&
// cleared CacheKey is only equal to itself
(key = this.get()) != null &&
// compare key by identity
key == ((CacheKey<K>) obj).get();
}
void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map,
ConcurrentMap<?, Boolean> reverseMap) {
// removing just by key is always safe here because after a CacheKey
// is cleared and enqueue-ed it is only equal to itself
// (see equals method)...
ConcurrentMap<?, ?> valuesMap = map.remove(this);
// remove also from reverseMap if needed
if (valuesMap != null) {
for (Object cacheValue : valuesMap.values()) {
reverseMap.remove(cacheValue);
}
}
}
}
/**
* 来看看我们的get方法
* 回忆一下我们是怎么调用它的
* proxyClassCache.get(loader, interfaces);
* key就是我们的classLoader, parameter是我们的接口数组
*/
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
//由于是第一次调用,所以new了一个新的cacheKey出来
Object cacheKey = CacheKey.valueOf(key, refQueue);
//由于是第一次调用,所以这里还是等于null
/**
* 再来分析一下这个map
* 这个map的声明是
* ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
* = new ConcurrentHashMap<>();
* 这三个字段分别是什么意思呢,我们从下面的putIfAbsent()可以看出
* 第一个就是CacheKey,是一个弱引用,下一次gc后不存在了
* 第二个的key是subKeyFactory.apply(key, parameter)产生的keyX对象
* 第二个的value就是产生我们代理类class的Supplier,其中的get方法返回的是代理类的class
* 如果我们已经看了下面就会发现第二个value就是CacheValue,也是一个弱引用,下一次gc后不存在了
* 那其实唯一的一个强引用就是keyX对象,但是keyX中的字段中的interfce[]数组还是弱引用的
* 如果经历过一次gc,那唯一存在的就是keyX中的hash值,那其实还是一个过期值。
*/
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//这里利用KeyFactory返回了一个KeyX对象
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//得到class的supplier
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
//这里是个循环,循环退出的条件是supplier.get()返回一个非null的值,这个值就是我们代理类的class
//这里的设计是考虑了并发的情况的
//factory的get方法是用来计算出class文件,然后放到缓存中
while (true) {
if (supplier != null) {
//如果得到了value,那么就返回
//这里有两种情况
//第一种就是已经在缓存中了,所以直接返回就好
//第二种就是缓存中没有它,这里的supplier其实已经被替换成了一个Factory(见下面的代码)
//然后调用factory的get方法同步得到一个class
V value = supplier.get();
if (value != null) {
return value;
}
}
//懒加载一个Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
//反复检查supplier是不是空
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
supplier = factory;
}
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else {
//如果已经有线程进行了处理并加到了缓存中
supplier = valuesMap.get(subKey);
}
}
}
}
下面看看Factory到底实现了什么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/**
* 从上面用法看这个Factory最终赋给了Supplier,所以这个类也是提供代理类class的
* 不过这个类的get方法比较特殊,是个同步方法
*/
private final class Factory implements Supplier<V> {
private final K key;
private final P parameter;
private final Object subKey;
private final ConcurrentMap<Object, Supplier<V>> valuesMap;
Factory(K key, P parameter, Object subKey,
ConcurrentMap<Object, Supplier<V>> valuesMap) {
this.key = key;
this.parameter = parameter;
this.subKey = subKey;
this.valuesMap = valuesMap;
}
//这个get方法是个同步的方法,用来保证在多线程环境下的并发安全
public synchronized V get() {
//再次检查一遍,这样第二个往后进来的线程就可以直接返回,得到缓存中的值
//避免重复计算
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
return null;
}
//现在supplier == this
V value = null;
try {
//这里产生新的代理类class
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) {
//如果value还是null,说明出现了失败
valuesMap.remove(subKey, this);
}
}
//还是检查一遍value不为空
assert value != null;
//把代理类class包装进CacheValue中
CacheValue<V> cacheValue = new CacheValue<>(value);
if (valuesMap.replace(subKey, this, cacheValue)) {
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
return value;
}
}
那是怎么处理过期的缓存呢,就是被gc收集走的那些缓存
在WeakCache#get中还执行了
1 | expungeStaleEntries(); |
至此,分析都差不多了,下面看看到底是怎么生成字节码的,在ProxyClassFactory的apply方法中
1 |
|
主要方法在ProxyGenerator#generateProxyClass方法中,返回的是一个二进制的class流byte[ ]
1 | public class ProxyGenerator { |
下面就不进行赘述了。