WeakHashMap 消除过期的对象引用 避免OOM内存泄漏
- 自己管理的内存(数组长度减小后,pop出的对象容易导致内存泄漏)
- 缓存
- 监听和回调
自己管理的内存
public class Stack{
private Object[] elements;
# private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack(){
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e){
ensureCapacity();
elements[size++]=e; // allocate新的堆内存和栈内存
}
public Object pop(){
if(size==0) throw new EmptyStackException();
return element[--size]; // pop出element[size],该对象不再有效。内存泄漏原因。
}
private void ensureCapacity(){
if(elements.length==size)
elements = Arrays.copyOf(elements, 2*size+1);
}
}
弹出的对象不再有效,但JVM不知道,所以会一直保持该对象,没有gc进行回收,造成内存泄露。
解决:
主动设置为null,就没有原来对象的引用了,如果其他地方没有引用那就会被回收
public Object pop(){
if(size==0) throw new EmptyStackException();
elements[size] = null; // 等待回收
return element[--size];
}
缓存
缓存的对象容易被程序员遗忘,需要设置机制来维护缓存,
例如不定期回收不再使用的缓存(使用定时器)。某些情况下,使用WeakHashMap可以达到缓存回收的功效。注,只有缓存依赖于外部环境,而不是依赖于值时,WeakHashMap才有效。
监听或回调
使用监听和回调要记住取消注册。确保回收的最好的实现是使用弱引用(weak reference),例如,只将他们保存成WeakHashMap的键。
WeakHashMap
弱引用:一个对象具有弱引用,垃圾回收的时候,一定会被回收;
WeakHashMap:当除了自身有对key的引用外,此key没有其他引用,那么GC之后此map会自动丢弃此值;
WekHashMap 适用于缓存,保存的对象只要其他地方没有它的引用,就会被回收;
HashMap作为强引用,不主动将key删除,保存的对象内容是不会被垃圾回收的;
案例:
参考 weakHashMap 用法 - 疯狂的小萝卜头 - 博客园 (cnblogs.com)
@Test
public void weakTest() {
String a = new String("a");
String b = new String("b");
Map weakmap = new WeakHashMap();
Map map = new HashMap();
map.put(a, "aaa");
map.put(b, "bbb");
weakmap.put(a, "aaa");
weakmap.put(b, "bbb");
//HashMap remove 掉对象a
map.remove(a);
/**
* 这里将a变量置为null,WeakHashMap中的a将自动被回收掉
* 因为对于a对象,hashmap主动删除了对象a,除了WeakHashMap中有对象a的引用,其他地方没有指向a的引用;
* 所以 WeakHashMap中的a将自动被回收掉
*/
a = null;
/**
* 对于b对象,虽然没有b变量指向b对象的地址,但是HashMap里面还有b对象的引用
* WeakHashMap中h还是会保留b对象的引用,而不会让gc回收
*/
b = null;
System.gc();
Iterator i = map.entrySet().iterator();
while (i.hasNext()) {
Map.Entry en = (Map.Entry) i.next();
System.out.println("map:" + en.getKey() + ":" + en.getValue());
}
Iterator j = weakmap.entrySet().iterator();
while (j.hasNext()) {
Map.Entry en = (Map.Entry) j.next();
System.out.println("weakmap:" + en.getKey() + ":" + en.getValue());
}
}
//结果
map:b:bbb
weakmap:b:bbb
声明了两个Map对象,一个是HashMap,一个是WeakHashMap,同时向两个map中放入a、b两个对象;
当HashMap remove掉a 并且将a、b都指向null时,WeakHashMap中的a将自动被回收掉。
出现这个状况的原因是,对于a对象而言,当HashMap remove掉并且将a指向null后,除了WeakHashMap中还保存a外已经没有指向a的指针了,所以WeakHashMap会自动舍弃掉a,
而对于b对象变量虽然指向了null,但HashMap中还有指向b的指针,所以WeakHashMap将会保留
总之,a对象最后只有WeakHashMap里面有引用被使用,其他地方没有,满足弱引用被回收的条件;
而b对象没有被HashMap主动remove,还是在HashMap里面有引用存在,所以不会被回收;
注意:不要使用基础类型作为WeakHashMap的key
案例:
@Test
public void weakHashMapTest() {
WeakHashMap<Object, Object> map = new WeakHashMap<>();
for (int i = 1; i <= 1000; i++) {
map.put(i,new Object());
System.gc();
System.out.println("插入第"+i+"个的时候,map size:"+map.size());
}
}
可以看到结果,最后大小只会停留在128左右;
因为使用int为key存入,会自动转为包装类Integer对象,维护了一个常量缓存,保存了-128到127的缓存,所以大于这个区间的对象,其他地方没有引用,只有WeakHashMap有引用,那就满足弱引用被垃圾回收的条件;
而属于这个区间的对象,在Integer的缓存里还有引用,就不会被垃圾回收;