Effective Java 读书笔记(四)
2021/6/6 20:26:07
本文主要是介绍Effective Java 读书笔记(四),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言:
又又来更新了,鸽王了害,自从工作了之后一直很少花时间看书了,最近搬了新的家环境一变,又有了点儿兴致hh,大概率更新的节奏是每周一篇吧,工作日要上班蛮累的。。。希望自己可以坚持下去吧~
Item07:Eliminate expired object references 消除过期的对象引用
JAVA中有自动的回收功能,但某些地方如果书写的代码不好仍然会出现内存泄漏。这里举个例子:栈实现
public class MemoryLeakStack { private Object[] elements; private int size; private static final int DEFAULT_INITIAL_CAPACITY=16; public MemoryLeakStack(){ elements=new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e){ ensureCapacity(); elements[size++]=e; } public Object pop(){ if (size==0){ throw new EmptyStackException(); } return elements[--size]; } private void ensureCapacity() { if (elements.length==size){ //返回一个新的数组对象,复制旧数组且扩大长度 elements= Arrays.copyOf(elements,2*size+1); } } }
一眼看去,貌似没啥问题,但是其实在pop方法中存在着安全隐患,它只是仅仅将stack的长度size–,而并没有将数组中的对象引用消除,导致会存在一直无法使用的对象存活在内存中。
这里的改进方式是:
public Object pop(){ if (size==0){ throw new EmptyStackException(); } Object result= elements[--size]; elements[size]=null; //消除引用 return result; }
虽然看起来很简单且java也提供了自动回收机制,但日常开发中还是要注意消除引用。
那么JAVA中有哪些场景需要注意的呢?
1、只要类是自己管理内存,就需要警惕。例如刚刚举的Stack例子
2、对象存放到缓存。
需要注意的是当对象引用放入缓存中,如果它很久不会被使用,那么该引用应该进行消除。解决方法:可以使用WeakHashMap代表缓存。
WeakHashMap细说
和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。
不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。某个键被移除时,它对应的键值对也就从映射中有效地移除了。
这个“弱键”的原理呢?大致上就是,通过WeakReference和ReferenceQueue实现的。
WeakHashMap类的重要内容:
public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> { //... Entry<K,V>[] table; private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); }
可以看出WeakHashMap是使用一个Enrty数组存储数据。
还有一个用于存储被gc回收后的弱引用的队列ReferenceQueue
WeakHashMap中的Enrty:
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { }
可以看出其继承了WeakReference类。代表着存储的Entry数组是一个软引用数组
到底是如何利用这个WeakReference以及ReferenceQueue呢?
WeakHashMap代码片段:
/** * Expunges stale entries from the table. 删除旧entry */ private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") //获取ReferenceQueue队列中元素的引用。即已经被gc清除的健值对 Entry<K,V> e = (Entry<K,V>) x; int i = indexFor(e.hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> p = prev; while (p != null) { Entry<K,V> next = p.next; //找到相同 if (p == e) { //链表删除操作 if (prev == e) table[i] = next; else prev.next = next; // Must not null out e.next; // stale entries may be in use by a HashIterator //将value的引用设置为null e.value = null; // Help GC size--; break; } prev = p; p = next; } } } }
具体的操作就是GC每次清理掉一个对象之后,引用对象会被放到ReferenceQueue中。expungeStaleEntries方法:遍历ReferenceQueue队列,将Enrty数组中对应的Entry删除,并且将其value设置为null加速Gc的过程。减少对value的引用,方便其后续被回收。
- 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。 - 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
- 当我们每次需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。
public class ReferenceQueueTest { public static void main(String[] args) throws InterruptedException { int _1M = 1024 * 1024; ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); Thread thread = new Thread(() -> { try { int cnt = 0; WeakReference<byte[]> k; while ((k = (WeakReference) referenceQueue.remove()) != null) { System.out.println((cnt++) + "回收了:" + k); } } catch (InterruptedException e) { // 结束循环 } }); //设置为守护线程,,当系统中全部都是守护线程的时候就会退出 thread.setDaemon(true); thread.start(); Object value = new Object(); Map<Object, Object> map = new HashMap<>(); for (int i = 0; i < 100; i++) { byte[] bytes = new byte[_1M]; WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes, referenceQueue); map.put(weakReference, value); } System.out.println("map.size->" + map.size()); } }
3、监听器以及其他回调。
这篇关于Effective Java 读书笔记(四)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27MQ底层原理资料详解:新手入门教程
- 2024-11-27MQ项目开发资料入门教程
- 2024-11-27RocketMQ源码资料详解:新手入门教程
- 2024-11-27本地多文件上传简易教程
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器
- 2024-11-26Java云原生资料:新手入门教程与实战指南
- 2024-11-26JAVA云原生资料入门教程