弱引用总结及HashMap、List弱引用并Lru实现

Reference

C++中有指针和Reference的概念,指针可以重新赋值,而Reference只能初始化时赋值。

然而,java中的Reference是可以重新赋值,并不是C++的Reference概念,类似于C++的指针的概念。


WeakReference和Strong Reference

通常实例化的操作就是强引用:

Object obj = new Object();

obj强引用new Object()在堆上分配的空间。

只有执行obj=null; 时,new Object()在堆上分配的内存才会失去引用,可以被GC回收。

弱引用声明:

WeakReference ref_obj = new WeakReference(new Object);

这样ref_obj弱引用new Object()在堆上分配的空间。

当GC被触发时,就会回收或者稍后回收heap空间。


HashMap(put方法)添加对象引用

@Override public V put(K key, V value) {
        if (key == null) {
            return putValueForNullKey(value);
        }

        int hash = Collections.secondaryHash(key);
        HashMapEntry[] tab = table;//---------------------HashMap数据存于HashMapEntry数组内。
        int index = hash & (tab.length - 1);
        for (HashMapEntry e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;//-----------------------Heap Object赋值给HashMapEntry的value变量。
                return oldValue;
            }
        }

        // No entry for (non-null) key is present; create one
        modCount++;
        if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
        }
        addNewEntry(key, value, hash, index);
        return null;
    }

static class HashMapEntry implements Entry { //----------实现Entry接口,并没有WeakReference。
        final K key;
        V value;
        final int hash;
        HashMapEntry next;
.... ...

综上,可以看出,HashMap是对K和V两个Heap Object的强引用。HashMap是通过remove(Object key)方法将key对应的Entry(Heap Object)所指向的引用,指向next Entry.从而使key对应的Entry失去引用,进而使其在GC触发时得到回收。

而WeakHashMap的put方法:

@Override
    public V put(K key, V value) {
        poll();
        int index = 0;
        Entry entry;
        if (key != null) {
            index = (Collections.secondaryHash(key) & 0x7FFFFFFF) % elementData.length;
            entry = elementData[index];//-----------------------------------------------WeakHashMap的数据依然存于数组:Entry[] elementData;
            while (entry != null && !key.equals(entry.get())) {
                entry = entry.next;
            }
        } else {
            entry = elementData[0];
            while (entry != null && !entry.isNull) {
                entry = entry.next;
            }
        }
        if (entry == null) {
            modCount++;
            if (++elementCount > threshold) {
                rehash();
                index = key == null ? 0 : (Collections.secondaryHash(key) & 0x7FFFFFFF)
                        % elementData.length;
            }
            entry = new Entry(key, value, referenceQueue);//-------------------Heap Object(key和value)赋给了Entry对象的key和value属性。
            entry.next = elementData[index];
            elementData[index] = entry;
            return null;
        }
        V result = entry.value;
        entry.value = value;
        return result;
    }
private static final class Entry extends WeakReference implements //-----------WeakHashMap中的Entry是继承WeakReference.
            Map.Entry {
        final int hash;

        boolean isNull;

        V value;

        Entry next;
... ...

所以,WeakHaspMap是对K和V的弱引用。



ArrayList(add方法)添加对象的引用

@Override public boolean add(E object) {
        Object[] a = array;
        int s = size;
        if (s == a.length) {
            Object[] newArray = new Object[s +
                    (s < (MIN_CAPACITY_INCREMENT / 2) ?
                     MIN_CAPACITY_INCREMENT : s >> 1)];//---------------ArrayList也是将数据存于数组。【LinkedList并不是存于数组】
            System.arraycopy(a, 0, newArray, 0, s);//-------------------这个方法对数组扩容,并保证添加顺序不变。
            array = a = newArray;
        }
        a[s] = object;//------------------------------Heap Object赋给了数组的最后一个位置。
        size = s + 1;
        modCount++;
        return true;
    }

@Override public void clear() {
        if (size != 0) {
            Arrays.fill(array, 0, size, null);//----------将null赋给array中从0到size位置的所有元素。
            size = 0;
            modCount++;
        }
    }
public static void fill(Object[] array, int start, int end, Object value) {
        Arrays.checkStartAndEnd(array.length, start, end);
        for (int i = start; i < end; i++) {
            array[i] = value;//------------------------------将start到end位置的array元素都赋值为null.
        }
    }

显然,ArrayList是对object的强引用。而clear()方法,是将所有引用置空,这样就是可以使E object在GC触发时得到回收。

而List并没有现成的WeakList。


LRU策略

LRU(Least Recently Used)是利用“强引用”LinkedHashMap(将accessOrder设为true)存储数据。

通过LinkedHashMap的eldest()方法获取到最老(使用次数最低)的Entry eldest,

然后通过LruCache类的put(K,V)方法将数据添加到LinkedHashMap map中,当map中所有V的大小总和大于LruCache所设置的最大值maxSize时,就调用map.remove(K)方法,使V(Heap Object)失去Entry的引用,以达到便于GC回收这部分内存的目的。

/**
     * True if access ordered, false if insertion ordered.
     */
    private final boolean accessOrder;//-------true,则按照访问的顺序排序。
public LinkedHashMap() {
        init();
        accessOrder = false;//------------------默认是false,按照插入的顺序排序。
    }
public final V put(K key, V value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }

        V previous;
        synchronized (this) {
            putCount++;
            size += safeSizeOf(key, value);
            previous = map.put(key, value);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }

        trimToSize(maxSize);//------------每次添加数据,都要判断缓存大小。
        return previous;
    }
public void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }

                if (size <= maxSize) {
                    break;
                }

                Map.Entry toEvict = map.eldest();
                if (toEvict == null) {
                    break;
                }

                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);//---------------------当size>maxSize了,则map.remove();
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }
@Override public V remove(Object key) {
        if (key == null) {
            return removeNullKey();
        }
        int hash = Collections.secondaryHash(key);
        HashMapEntry[] tab = table;
        int index = hash & (tab.length - 1);
        for (HashMapEntry e = tab[index], prev = null;
                e != null; prev = e, e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                if (prev == null) {
                    tab[index] = e.next;
                } else {
                    prev.next = e.next;
                }
                modCount++;
                size--;
                postRemove(e);//-----------------------------HashMap的remove后,调用了空实现方法postRemove();
                return e.value;
            }
        }
        return null;
    }
@Override void postRemove(HashMapEntry e) {//----------------LinkedHashMap方法覆写了postRemove方法,
        LinkedEntry le = (LinkedEntry) e;
        le.prv.nxt = le.nxt;
        le.nxt.prv = le.prv;
        le.nxt = le.prv = null; // Help the GC (for performance)//-------------将Heap Object失去LinkedEntry le的引用,以便GC回收。
    }
Lru策略,最终只是将Heap Object失去引用,并未强制触发GC,所以LruCache的maxSize的设定不应该接近android虚拟机的最大Heap Size,否则同样容易引起内存溢出!!!







你可能感兴趣的:(ANDROID,JAVA)