标签:一个 nal rar exception RoCE col guid rom 循环调用
我们在日常工作中,对于一些没有固定销毁时间点的对象,通常会考虑用WeakHashMap 来协助自动销毁对象。
举个例子,根据不同的request出错信息的key,自动找到相关的翻译内容。就是常说的国际化,I18n。软件出错的信息是不固定的,如果每次load完内容,就销毁又性能不高。所以通常想法是做个map,但是map的情况下,我没有办法决定什么时候销毁这个key,那么这个时候通常会用WeakHashMap。利用GC 来帮我保证map的内容不会膨胀,导致内存泄漏。
WeakHashMap的行为取决于垃圾回收器的动作。由于垃圾回收器是由jvm调度的,gc可以发生在WeakHashMap对象生命周期的任何时候,所以WeakHashMap的表现为,
总而言之就是只要在垃圾回收器清除某个键的弱引用之后,该键才会自动移除。所以如果用WeakHashMap自己要有这方面的判断。比如上例,如果拿不到内容需要重新加载再放到WeakHashMap。
如果一个对象具有弱引用,在GC线程扫描内存区域的过程中,不管当前内存空间足够与否,都会回收内存,使用弱引用 构建非敏感数据的缓存。声明如下:
//WeakReference WeakReference<Object> wf = new WeakReference<~>(new Object());
WeakHashMap中Entry[]有着Entry->WeakReference->Reference这样一个继承结构,我们先来看下Reference
private static Lock lock = new Lock(); /* List of References waiting to be enqueued. The collector adds * References to this list, while the Reference-handler thread removes * them. This list is protected by the above lock object. The * list uses the discovered field to link its elements. */ private static Reference<Object> pending = null; /* High-priority thread to enqueue pending References */ private static class ReferenceHandler extends Thread { private static void ensureClassInitialized(Class<?> clazz) { try { Class.forName(clazz.getName(), true, clazz.getClassLoader()); } catch (ClassNotFoundException e) { throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e); } } static { // pre-load and initialize InterruptedException and Cleaner classes // so that we don‘t get into trouble later in the run loop if there‘s // memory shortage while loading/initializing them lazily. ensureClassInitialized(InterruptedException.class); ensureClassInitialized(Cleaner.class); } ReferenceHandler(ThreadGroup g, String name) { super(g, name); } public void run() { while (true) { tryHandlePending(true); } } }
上面是Reference类的部分代码,可以看到Reference中有一个全局的锁对象:lock;有一个静态变量pending;在静态代码块中启动一个ReferenceHandler线程,启动完成后处于wait状态,它在一个Lock同步锁模块中等待。那么WeakHashMap中key/value如何自动回收跟这些有什么关系呢。
我们假设JVM使用cms收集器(使用其他收集器对于弱引用的回收原理相同)。
这是WeakHashMap中的ReferenceQueue定义,注释就可以知道这是用来清除WeakEntries的
/** * Expunges stale entries from the table. */ private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") 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 e.value = null; // Help GC size--; break; } prev = p; p = next; } } } }
大家可以看一段GC 的log,看看reference 是怎么回收的。
继续观察日志,可以发现,总共150ms的执行过程中,仅Ref Proc一步就花费了108ms。那么可以判断,多出来的时间,应该是和Ref Proc 有关了。Ref Proc这一步就来处理这些引用对象。默认是由单线程执行,如果这一步花费的时间较长,可以通过加参数-XX:+ParallelRefProcEnabled改为多线程处理。
使用WeakHashMap一般是全局变量,我们直接的想法就是把它应用到多线程中去。观察WeakHashMap源码可以发现,它是线程不安全的
1 /** 2 * Expunges stale entries from the table. 3 */ 4 private void expungeStaleEntries() { 5 for (Object x; (x = queue.poll()) != null; ) { 6 synchronized (queue) { 7 @SuppressWarnings("unchecked") 8 Entry<K,V> e = (Entry<K,V>) x; 9 int i = indexFor(e.hash, table.length); 10 Entry<K,V> prev = table[i]; 11 Entry<K,V> p = prev; 12 while (p != null) { 13 Entry<K,V> next = p.next; 14 if (p == e) { 15 if (prev == e) 16 table[i] = next; 17 else 18 prev.next = next; 19 // Must not null out e.next; 20 // stale entries may be in use by a HashIterator 21 e.value = null; // Help GC 22 size--; 23 break; 24 } 25 prev = p; 26 p = next; 27 } 28 } 29 } 30 }
下面指针交换过程,有可能把另外一个线程修改的指针改到当前线程指针, 导致Table的数组不正确。从而引起无限循环。
prev = p;
p = next;
这段代码当线程1
pre =p
p =next 时
外一个线程可能
pre=p(此时 p 可能是线程1的 p.pre)
当线程1 p=next 付完值后, 线程2 又开始赋值 p=p.pre.next 所以p=p 就造成指针闭环。当你去调用get 方法是,由于有 while 条件和e =e.next就导致无限循环。
*/ public V get(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int index = indexFor(h, tab.length); Entry<K,V> e = tab[index]; while (e != null) { if (e.hash == h && eq(k, e.get())) return e.value; e = e.next; } return null; }
所以当我们使用WeakHashMap做temporary cache时, 通常都要如下写法
WeakHashMap<String, String> weakHashMapintsmaze=new WeakHashMap<String, String>(); Map<String, String> intsmaze=Collections.synchronizedMap(weakHashMapintsmaze)
标签:一个 nal rar exception RoCE col guid rom 循环调用
原文地址:https://www.cnblogs.com/developernotes/p/14696624.html