- 浏览: 259653 次
- 性别:
- 来自: 北京
-
博客专栏
-
-
Java并发包源码解析
浏览量:103587
最新评论
-
746238836:
整个RingBuffer内部做了大量的缓存行填充,前后各填充了 ...
disruptor-3.3.2源码解析(2)-队列 -
xiangshouxiyang:
群加不了。。。
Jdk1.7 ForkJoin框架源码解析汇总 -
有贝无患:
acquire方法里面为什么tryAcquire会被调用多次 ...
Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer -
zwy_qz:
library_call.cpp 里面的内联操作 inline ...
Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX -
sunwang810812:
您好,正在学习您的文章,中间有一段,一直没明白:“privat ...
Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer
总结这个类之前,首先看一下Java引用的相关知识。Java的引用分为四种:强引用、软引用、弱引用和虚引用。
强引用:就是常见的代码中的引用,如Object o = new Object();存在强引用的对象不会被垃圾收集器回收。所以我们会在一些代码中看到显示切断强引用,以便回收相关对象的情况。
软引用:比强引用弱一些,表示对一些有用但非必须对象的引用。这些对象会在将要发生内存溢出异常之前被回收。在Java中用java.lang.ref.SoftReference来表示软引用。看一个例子,先设置下JVM参数,将堆大小设为10m,老年代新生代各5m,打印出GC日志。
JVM参数
输出结果:
基本过程是这样,程序先创建了一个3M的对象_3m_1,这个对象被分配到新生代里面,然后创了一个软引用关联到_3m_1,然后切断_3m_1的强引用。接下来又创建了一个3M的对象_3m_2,_3m_2要试图分配到新生代,这时发现新生代剩余空间不足(一共4608K,已经有一个3M的对象在里面)。接下来触发了一次新生代的垃圾收集动作(Minor GC),但这次收集没有回收掉_3m_1。然后_3m_1晋升到老年代,_3m_2被分配到新生代。接下来程序显示触发了一次Full GC,但这次Full GC似乎没起什么作用,_3m_1和_3m_2都安然无恙。
那么修改一下程序,再跑一次看看:
输出结果:
前面的过程和上面类似,区别在创建第三个3M对象_3m_3时,首先触发了一次Minor GC,没回收什么东西,但是要把新生代里的_3m_2放到老年代,这样才能腾出地方给_3m_3。但是老年代已经有_3m_1了,剩余的空间不足以放下_3m_2,这时系统触发了Full GC(不用我们触发system Full GC了)。从日志上看似乎回收掉了3M的空间。说明在内存溢出之前,软引用关联的对象被回收了。这便是软引用的特性,如果这里是一个强引用的话,那么便会出现OOM了。
弱引用:比软引用还弱一些,所以关联的对象在下一次垃圾收集的时候就会被回收掉。继续看例子,JVM参数同上。
输出结果:
虚引用:虚引用一般用来代替finalize方法来做一些引用对象被回收前的清理动作(由于finalize的不确定性,不建议使用)。虚引用必须配合一个ReferenceQueue一起使用,在GC决定要回收其引用对象时(引用对象没有任何强引用关联),虚引用会入栈,程序中可以在这个时机做一些清理工作。虚引用的get方法返回null:
这样保证了虚引用的关联对象永远不可能通过get方法再次获得强引用。但虚引用的关联对象要一直等到虚引用本身不可达或者被回收时才能够被回收,这点不同于软引用和弱引用。
最后说一下java.lang.ref.ReferenceQueue。ReferenceQueue是一个引用队列,构造一个软引用、弱引用或者虚引用可以传入一个ReferenceQueue,表示这个引用注册到传入的引用队列上,简单的说,当GC决定回收引用关联对象时,会将这个引用放到引用队列里。
大概了解了Java引用的相关内容,现在可以看下java.util.WeakHashMap的源码了。
整体看下来,其实和HashMap差不多,所以只看一下差异的地方。
WeakHashMap中多了一个引用队列的变量,大概能猜到是干什么的了吧。
看下WeakHashMap中的Entry:
java.util.WeakHashMap.Entry扩展自java.lang.ref.WeakReference,将Key(Map中的键)作为虚引用的关联对象。
由于WeakHashMap本身允许键值为null,所以这里不同于HashMap,需要利用一些转化函数。
由于虚引用的性质,当WeakHashMap中的某个Key已经没有外部的强引用,那么在接下来发生的垃圾收集动作里,它将会被回收。这里要注意一下,回收的仅仅是Key,但是Entry还在呢(也可以说是虚引用本身)。
所以在一些操作里会调用expungeStaleEntries方法,这个方法里会清理所有Key已经被回收的Entry。
基本上差异就是这些。WeakHashMap本身的特性也使它经常被应用到一些缓存的场景。
强引用:就是常见的代码中的引用,如Object o = new Object();存在强引用的对象不会被垃圾收集器回收。所以我们会在一些代码中看到显示切断强引用,以便回收相关对象的情况。
public void clear() { modCount++; // Let gc do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
软引用:比强引用弱一些,表示对一些有用但非必须对象的引用。这些对象会在将要发生内存溢出异常之前被回收。在Java中用java.lang.ref.SoftReference来表示软引用。看一个例子,先设置下JVM参数,将堆大小设为10m,老年代新生代各5m,打印出GC日志。
JVM参数
-Xms10m -Xmx10m -Xmn5m -XX:+PrintGCDetails
public static void main(String[] args) { //构造一个3M的对象 Object _3m_1 = new byte[1024 * 1024 * 3]; //创建软引用 SoftReference<Object> reference = new SoftReference<Object>(_3m_1); //切断强引用 _3m_1 = null; //再构造一个3M的对象 Object _3m_2 = new byte[1024 * 1024 * 3]; //触发Full GC System.gc(); System.out.println("软引用关联对象:"+reference.get()); }
输出结果:
[GC [DefNew: 3325K->152K(4608K), 0.0023022 secs] 3325K->3224K(9728K), 0.0023380 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System) [Tenured: 3072K->3072K(5120K), 0.0057404 secs] 6378K->6296K(9728K), [Perm : 380K->380K(12288K)], 0.0057837 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 软引用关联对象:[B@14318bb Heap def new generation total 4608K, used 3350K [0x31fe0000, 0x324e0000, 0x324e0000) eden space 4096K, 81% used [0x31fe0000, 0x32325880, 0x323e0000) from space 512K, 0% used [0x32460000, 0x32460000, 0x324e0000) to space 512K, 0% used [0x323e0000, 0x323e0000, 0x32460000) tenured generation total 5120K, used 3072K [0x324e0000, 0x329e0000, 0x329e0000) the space 5120K, 60% used [0x324e0000, 0x327e0010, 0x327e0200, 0x329e0000) compacting perm gen total 12288K, used 380K [0x329e0000, 0x335e0000, 0x369e0000)
基本过程是这样,程序先创建了一个3M的对象_3m_1,这个对象被分配到新生代里面,然后创了一个软引用关联到_3m_1,然后切断_3m_1的强引用。接下来又创建了一个3M的对象_3m_2,_3m_2要试图分配到新生代,这时发现新生代剩余空间不足(一共4608K,已经有一个3M的对象在里面)。接下来触发了一次新生代的垃圾收集动作(Minor GC),但这次收集没有回收掉_3m_1。然后_3m_1晋升到老年代,_3m_2被分配到新生代。接下来程序显示触发了一次Full GC,但这次Full GC似乎没起什么作用,_3m_1和_3m_2都安然无恙。
那么修改一下程序,再跑一次看看:
public static void main(String[] args) { //构造一个3M的对象 Object _3m_1 = new byte[1024 * 1024 * 3]; //创建软引用 SoftReference<Object> reference = new SoftReference<Object>(_3m_1); //切断强引用 _3m_1 = null; //再构造一个3M的对象 Object _3m_2 = new byte[1024 * 1024 * 3]; //又构造一个3M的对象 Object _3m_3 = new byte[1024 * 1024 * 3]; System.out.println("软引用关联对象:"+reference.get()); }
输出结果:
[GC [DefNew: 3325K->152K(4608K), 0.0021553 secs] 3325K->3224K(9728K), 0.0021908 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC [DefNew: 3224K->3224K(4608K), 0.0000288 secs][Tenured: 3072K->3072K(5120K), 0.0060759 secs] 6296K->6296K(9728K), [Perm : 380K->380K(12288K)], 0.0061594 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC [Tenured: 3072K->3210K(5120K), 0.0049693 secs] 6296K->3210K(9728K), [Perm : 380K->375K(12288K)], 0.0050152 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 软引用关联对象:null Heap def new generation total 4608K, used 3280K [0x31fe0000, 0x324e0000, 0x324e0000) eden space 4096K, 80% used [0x31fe0000, 0x32314050, 0x323e0000) from space 512K, 0% used [0x32460000, 0x32460000, 0x324e0000) to space 512K, 0% used [0x323e0000, 0x323e0000, 0x32460000) tenured generation total 5120K, used 3210K [0x324e0000, 0x329e0000, 0x329e0000) the space 5120K, 62% used [0x324e0000, 0x32802b28, 0x32802c00, 0x329e0000)
前面的过程和上面类似,区别在创建第三个3M对象_3m_3时,首先触发了一次Minor GC,没回收什么东西,但是要把新生代里的_3m_2放到老年代,这样才能腾出地方给_3m_3。但是老年代已经有_3m_1了,剩余的空间不足以放下_3m_2,这时系统触发了Full GC(不用我们触发system Full GC了)。从日志上看似乎回收掉了3M的空间。说明在内存溢出之前,软引用关联的对象被回收了。这便是软引用的特性,如果这里是一个强引用的话,那么便会出现OOM了。
弱引用:比软引用还弱一些,所以关联的对象在下一次垃圾收集的时候就会被回收掉。继续看例子,JVM参数同上。
public static void main(String[] args) { //构造一个3M的对象 Object _3m_1 = new byte[1024 * 1024 * 3]; //创建软引用 WeakReference<Object> reference = new WeakReference<Object>(_3m_1); //切断强引用 _3m_1 = null; //触发Full GC System.gc(); System.out.println("软引用关联对象:"+reference.get()); }
输出结果:
[Full GC (System) [Tenured: 0K->152K(5120K), 0.0086388 secs] 3325K->152K(9728K), [Perm : 380K->380K(12288K)], 0.0086922 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 软引用关联对象:null Heap def new generation total 4608K, used 208K [0x31fe0000, 0x324e0000, 0x324e0000) eden space 4096K, 5% used [0x31fe0000, 0x32014040, 0x323e0000) from space 512K, 0% used [0x323e0000, 0x323e0000, 0x32460000) to space 512K, 0% used [0x32460000, 0x32460000, 0x324e0000) tenured generation total 5120K, used 152K [0x324e0000, 0x329e0000, 0x329e0000) the space 5120K, 2% used [0x324e0000, 0x32506088, 0x32506200, 0x329e0000)
虚引用:虚引用一般用来代替finalize方法来做一些引用对象被回收前的清理动作(由于finalize的不确定性,不建议使用)。虚引用必须配合一个ReferenceQueue一起使用,在GC决定要回收其引用对象时(引用对象没有任何强引用关联),虚引用会入栈,程序中可以在这个时机做一些清理工作。虚引用的get方法返回null:
public class PhantomReference<T> extends Reference<T> { /** * Returns this reference object's referent. Because the referent of a * phantom reference is always inaccessible, this method always returns * <code>null</code>. * * @return <code>null</code> */ public T get() { return null; }
这样保证了虚引用的关联对象永远不可能通过get方法再次获得强引用。但虚引用的关联对象要一直等到虚引用本身不可达或者被回收时才能够被回收,这点不同于软引用和弱引用。
最后说一下java.lang.ref.ReferenceQueue。ReferenceQueue是一个引用队列,构造一个软引用、弱引用或者虚引用可以传入一个ReferenceQueue,表示这个引用注册到传入的引用队列上,简单的说,当GC决定回收引用关联对象时,会将这个引用放到引用队列里。
大概了解了Java引用的相关内容,现在可以看下java.util.WeakHashMap的源码了。
整体看下来,其实和HashMap差不多,所以只看一下差异的地方。
/** * Reference queue for cleared WeakEntries */ private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
WeakHashMap中多了一个引用队列的变量,大概能猜到是干什么的了吧。
看下WeakHashMap中的Entry:
/** * The entries in this hash table extend WeakReference, using its main ref * field as the key. */ private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> { private V value; private final int hash; private Entry<K,V> next; /** * Creates new entry. */ Entry(K key, V value, ReferenceQueue<K> queue, int hash, Entry<K,V> next) { super(key, queue); this.value = value; this.hash = hash; this.next = next; } public K getKey() { return WeakHashMap.<K>unmaskNull(get()); } public V getValue() { return value; } public V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public int hashCode() { Object k = getKey(); Object v = getValue(); return ((k==null ? 0 : k.hashCode()) ^ (v==null ? 0 : v.hashCode())); } public String toString() { return getKey() + "=" + getValue(); } }
java.util.WeakHashMap.Entry扩展自java.lang.ref.WeakReference,将Key(Map中的键)作为虚引用的关联对象。
由于WeakHashMap本身允许键值为null,所以这里不同于HashMap,需要利用一些转化函数。
/** * Value representing null keys inside tables. */ private static final Object NULL_KEY = new Object(); /** * Use NULL_KEY for key if it is null. */ private static Object maskNull(Object key) { return (key == null ? NULL_KEY : key); } /** * Returns internal representation of null key back to caller as null. */ private static <K> K unmaskNull(Object key) { return (K) (key == NULL_KEY ? null : key); }
由于虚引用的性质,当WeakHashMap中的某个Key已经没有外部的强引用,那么在接下来发生的垃圾收集动作里,它将会被回收。这里要注意一下,回收的仅仅是Key,但是Entry还在呢(也可以说是虚引用本身)。
所以在一些操作里会调用expungeStaleEntries方法,这个方法里会清理所有Key已经被回收的Entry。
/** * Expunges stale entries from the table. */ private void expungeStaleEntries() { Entry<K,V> e; while ( (e = (Entry<K,V>) queue.poll()) != null) { int h = e.hash; int i = indexFor(h, 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; e.next = null; // Help GC e.value = null; // " " size--; break; } prev = p; p = next; } } } /** * Returns the table after first expunging stale entries. */ private Entry[] getTable() { expungeStaleEntries(); return table; } /** * Returns the number of key-value mappings in this map. * This result is a snapshot, and may not reflect unprocessed * entries that will be removed before next attempted access * because they are no longer referenced. */ public int size() { if (size == 0) return 0; expungeStaleEntries(); return size; }
基本上差异就是这些。WeakHashMap本身的特性也使它经常被应用到一些缓存的场景。
发表评论
-
Jdk1.6 Collections Framework源码解析(12)-TreeMap、TreeSet
2016-01-03 16:06 2213Jdk1.6 Collections Framework ... -
Jdk1.6 Collections Framework源码解析(11)-EnumSet
2015-12-29 18:25 1916Jdk1.6 Collections Framework源 ... -
Jdk1.6 JUC源码解析(26)-ConcurrentSkipListMap、ConcurrentSkipListSet
2015-11-03 03:08 5472Jdk1.6 JUC源码解析(26)-Concurrent ... -
Jdk1.6 JUC源码解析(25)-ConcurrentHashMap
2015-10-30 19:02 2605Jdk1.6 JUC源码解析(25)-Co ... -
Jdk1.6 集合框架源码解析汇总
2015-10-29 22:05 3525Jdk1.6 集合框架源码解析汇总 非并发: ... -
Jdk1.6 JUC源码解析(24)-ConcurrentLinkedQueue
2015-10-29 19:02 1988Jdk1.6 JUC源码解析(24)-ConcurrentL ... -
Jdk1.6 JUC源码解析(23)-CopyOnWriteArrayList、CopyOnWriteArraySet
2015-10-29 18:55 1895Jdk1.6 JUC源码解析(23)-Cop ... -
Jdk1.6 JUC源码解析(22)-LinkedBlockingDeque
2015-10-29 18:47 1698Jdk1.6 JUC源码解析(22)-LinkedBloc ... -
Jdk1.6 JUC源码解析(18)-DelayQueue
2015-10-27 19:25 2460Jdk1.6 JUC源码解析(18)-DelayQueue ... -
Jdk1.6 JUC源码解析(15)-SynchronousQueue
2015-10-26 19:19 2631Jdk1.6 JUC源码解析(15)-Synchronou ... -
Jdk1.6 JUC源码解析(14)-PriorityBlockingQueue
2015-10-25 03:22 2368Jdk1.6 JUC源码解析(14)-Pr ... -
Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue
2015-10-24 22:28 1886Jdk1.6 JUC源码解析(13)-LinkedBloc ... -
Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue
2015-10-23 20:03 2241Jdk1.6 JUC源码解析(12)-Ar ... -
Jdk1.6 Collections Framework源码解析(10)-EnumMap
2013-09-09 14:55 1511看这个类之前,先看一下Java的枚举——Enu ... -
Jdk1.6 Collections Framework源码解析(9)-PriorityQueue
2013-09-03 20:37 2118开发中有时会遇到这样的情况。要求某个调度器去调 ... -
Jdk1.6 Collections Framework源码解析(7)-HashSet、LinkedHashSet
2013-08-28 11:34 1707本篇总结一 ... -
Jdk1.6 Collections Framework源码解析(6)-IdentityHashMap
2013-08-27 14:10 1639这篇总结一下java.util.Identit ... -
Jdk1.6 Collections Framework源码解析(5)-LinkedHashMap
2013-08-20 14:28 1825前面总结了java.util.HashMap, ... -
Jdk1.6 Collections Framework源码解析(4)-HashMap
2013-08-19 13:59 2056开发中常常 ... -
Jdk1.6 Collections Framework源码解析(3)-ArrayDeque
2013-08-12 10:59 3731表、栈和队列是三种基本的数据结构,前面总结的A ...
相关推荐
### Java Collections Framework 知识点概览 #### 一、教程导引与适用对象 - **教程概述**:本教程由 developerWorks 提供,旨在帮助读者深入理解 Java Collections Framework 的各个方面。 - **适用人群**: - *...
街道级行政区划shp矢量数据,wgs84坐标系,下载直接使用
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
街道级行政区划shp矢量数据,wgs84坐标系,下载直接使用
轻量级密码算法LBlock的FPGA优化实现.docx
街道级行政区划shp矢量数据,wgs84坐标系,下载直接使用
Git 资料 progit-zh-v2.1.1.pdf
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
篮球计分器FPGA附程序..doc
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
内容概要:本文档全面介绍了Linux开发的基础知识、应用场景、环境搭建、常用命令、Shell脚本编程以及C/C++和Python开发等内容。首先阐述了Linux开发的重要性及其在服务器端开发、嵌入式开发和系统运维等领域的广泛应用。接着详细讲解了如何选择合适的Linux发行版并安装系统,配置开发环境,包括安装必要的开发工具和配置SSH服务。文档还深入讲解了Linux基础命令,如文件和目录操作、文件内容查看与编辑、进程管理和权限管理。此外,介绍了Shell脚本编程的基本语法,包括变量、条件语句、循环语句和函数定义。针对C/C++和Python开发,文档分别讲解了编译器安装、程序编写与编译、调试方法及使用虚拟环境等内容。最后,简要介绍了Linux内核开发的相关知识,包括下载编译内核、内核模块开发等,并推荐了相关学习资源。 适合人群:对Linux开发感兴趣的初学者及有一定经验的研发人员,尤其是希望深入掌握Linux开发技能的开发者。 使用场景及目标:①掌握Linux开发环境的搭建与配置;②熟悉Linux基础命令和Shell脚本编程;③学习C/C++和Python在Linux下的开发流程;④了解Linux内核开发的基本概念和技术。 阅读建议:此文档内容丰富,涵盖面广,建议读者根据自身需求选择性阅读,并结合实际操作进行练习。特别是对于初学者,应先掌握基础命令和开发环境的搭建,再逐步深入到编程语言和内核开发的学习。
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
街道级行政区划shp数据,wgs84坐标系,直接使用。
内容概要:本文档《word练习题.docx》是一份详细的Word操作练习指南,涵盖了从基础到高级的各种功能。文档分为三个主要部分:内容编辑、页面布局和高效文档。内容编辑部分包括文本格式化、段落设置、项目编号、制表位、边框与底纹等练习;页面布局部分涉及分节符、分栏、页眉页脚、水印等设置;高效文档部分则聚焦于样式管理、导航窗格、题注、书签、超级链接、脚注与尾注、交叉引用等功能。每个练习都有具体的操作步骤,帮助用户掌握Word的各种实用技巧。 适合人群:适用于Word初学者及希望提高Word技能的中级用户,尤其是需要频繁使用Word进行文档编辑和排版的办公人员。 使用场景及目标:①帮助用户熟悉Word的基本操作,如文本编辑、格式设置等;②提升用户的文档排版能力,学会设置复杂的页面布局;③提高工作效率,掌握高效文档管理技巧,如样式应用、题注和交叉引用等。 其他说明:此文档不仅提供了具体的练习题目,还附带了详细的步骤说明,用户可以根据指引逐步完成每个练习。此外,文档中的一些练习涉及到智能文档和Office智能客户端的应用,有助于用户了解Word在企业级应用中的潜力。建议用户按照章节顺序逐步学习,实践每一个练习,以达到最佳的学习效果。
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
全球腐败感知数据(2000-2023)——3000行 33个指标 关于数据集 该数据集包含3000行和33列,涵盖了2000年至2023年的腐败感知指数(CPI)数据和各种治理指标。它包括国家排名、分数和其他指标,如公共部门腐败、司法腐败、贿赂指数、商业道德、民主指数、法治、政府效率、经济指标和人类发展指数。 这些数据可用于: 腐败趋势分析 腐败对GDP、人类发展指数和治理的影响 跨国比较 数据可视化和机器学习模型 该数据集对研究人员、数据分析师、政策制定者和对研究全球腐败趋势非常有用。
毕业设计(论文) 基于FPGA的数字频率计设计.doc
街道级行政区划shp数据,wgs84坐标系,直接使用。
NTI1NDU3NTAyODMwOTQxMzI0M18xNzQ0Nzk1MTk1OTgz_6.JPG