`

java-各种引用介绍

    博客分类:
  • java
 
阅读更多

java中有四种类型的引用,关于引用的类在java.lang.ref包下,其类图如下:


 

各种引用类型介绍

⑴强引用(StrongReference)

    强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

 

⑵软引用(SoftReference)

    如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

 

⑶弱引用(WeakReference)

    弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

    弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

 

⑷虚引用(PhantomReference)

    “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

    虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

 

 

 

示列代码如下:

	public void test() {
		//MyDate finalRef = new MyDate();
		MyDate softRef = new MyDate();
		MyDate weakRef = new MyDate();
		MyDate phantomRef = new MyDate();
		
		ReferenceQueue<MyDate> softQueue = new ReferenceQueue<MyDate>();
		ReferenceQueue<MyDate> weakQueue = new ReferenceQueue<MyDate>();
		ReferenceQueue<MyDate> phantomQueue = new ReferenceQueue<MyDate>();
		
		SoftReference<MyDate> soft = new SoftReference<MyDate>(softRef, softQueue);
		WeakReference<MyDate> weak = new WeakReference<MyDate>(weakRef, weakQueue);
		PhantomReference<MyDate> phantom = new PhantomReference<MyDate>(phantomRef, phantomQueue);
		
		softRef = null;
		weakRef = null;
		phantomRef = null;
		
		print(soft);
		print(weak);
		print(phantom);
		System.out.println("phantom.isEnqueued:"+phantom.isEnqueued());
		
		System.gc();
		System.out.println("=============================================================");
		
		print(soft);
		print(weak);
		print(phantom);
		//phantom.enqueue();
		System.out.println("phantom.isEnqueued:"+phantom.isEnqueued());
		
		System.gc();
		System.gc();
		System.gc();
		System.gc();
		System.gc();

		System.out.println("=============================================================");
		print(phantom);
		System.out.println("phantom.isEnqueued:"+phantom.isEnqueued());
	}
	
	public void print(Reference<MyDate> ref) {
		MyDate obj = ref.get();
		System.out.println("ref = "+ref+"\tobj="+obj);
	}

打印结果如下:

ref = java.lang.ref.SoftReference@61de33	obj=Date: 1399972627546
ref = java.lang.ref.WeakReference@14318bb	obj=Date: 1399972627546
ref = java.lang.ref.PhantomReference@ca0b6	obj=null
phantom.isEnqueued:false
=============================================================
ref = java.lang.ref.SoftReference@61de33	obj=Date: 1399972627546
ref = java.lang.ref.WeakReference@14318bb	obj=null
ref = java.lang.ref.PhantomReference@ca0b6	obj=null
obj [Date: 1399972627546] is gc
phantom.isEnqueued:false
obj [Date: 1399972627546] is gc
=============================================================
ref = java.lang.ref.PhantomReference@ca0b6	obj=null
phantom.isEnqueued:true

 多次GC之后,虚引用被加入到引用队列中

 

虚引用会引起OOM

	public void test() {
		Reference<MyRef>[] referent = new PhantomReference[100000];
		ReferenceQueue<MyRef> queue = new ReferenceQueue<MyRef>();

		for (int i = 0; i < referent.length; i++) {
		     referent[i] = new PhantomReference<MyRef>(new MyRef(), queue);// throw
		}
		System.out.println(referent[referent.length-1].get()); 
	}

 设置最大堆内存为2M,最后打印:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at test.ref.TestPhantom.test(TestPhantom.java:21)
	at test.ref.TestPhantom.main(TestPhantom.java:11)

 

 

 

引用类型总结

引用类型 获取引用对象方式 引用对象回收条件 是否会造成OOM
强引用 直接获取 不回收
软引用 通过引用对象的get() 内存满时
弱引用 通过引用对象的get() 垃圾回收时
虚引用 无法获得 不回收

 

 

JDK中的后台线程:

java.lang.ref.Reference$ReferenceHandler      

    当对象被回收时,虚拟机触发这个引用线程,这个线程用来处理各种类型的引用,并将引用加入到引用

   队列中

 

java.lang.ref.Finalizer$FinalizerThread

     当检查到有强引用被加入到队列后,就从队列中取出引用并,之后调用finalize()方法。并将强引用队列的前后引用关系清空

 

引用队列和两个后台线程执行图如下:


在这个图中, 各种引用被放入到一个队列中,这是一个双向队列,由后台线ReferenceHandler负责处理这个队列,将不同的引用加入到相应的队列中,并将强引用加入到强引用队列中。

此时FinalizerThread检查到有引用加入到队列中了,就将其从最上方的引用队列中删除,然后调用Object#finalize()方法。

 

 

 ReferenceHandler核心逻辑如下:    

	public void run() {
	    for (;;) {
		Reference r;
		synchronized (lock) {
		    if (pending != null) {
			r = pending;
			Reference rn = r.next;
			pending = (rn == r) ? null : rn;
			r.next = r;
		    } else {
			try {
			    lock.wait();
			} catch (InterruptedException x) { }
			continue;
		    }
		}
		// Fast path for cleaners
		if (r instanceof Cleaner) {
		    ((Cleaner)r).clean();
		    continue;
		}

		ReferenceQueue q = r.queue;
		if (q != ReferenceQueue.NULL) q.enqueue(r);
	    }
	}

 

FinalizerThread核心逻辑和引用队列的逻辑:

	public void run() {
	    for (;;) {
		try {
		    Finalizer f = (Finalizer)queue.remove();
		    f.runFinalizer();
		} catch (InterruptedException x) {
		    continue;
		}
	    }
	}

//引用队列会阻塞获取:
    public Reference<? extends T> remove(long timeout)
	throws IllegalArgumentException, InterruptedException
    {
	if (timeout < 0) {
	    throw new IllegalArgumentException("Negative timeout value");
	}
	synchronized (lock) {
	    Reference<? extends T> r = reallyPoll();
	    if (r != null) return r;
	    for (;;) {
		lock.wait(timeout);
		r = reallyPoll();
		if (r != null) return r;
		if (timeout != 0) return null;
	    }
	}
    }

 通过jstack打印出的线程堆栈:


 

 

 

最后还有一个WeakHashMap,它的键是弱引用类型,值为强引用,当键的引用被回收后,这个KV对就会被删除,WeakHashMap中的Entry,就将Key包装成WeakReference,将加入到弱引用队列中,

每次调用get都会对弱引用队列做检查,如果有数据则将其删除,其实现函数是expungeStaleEntries()

	public void weak() {
		Map<MyRef, Object> weakmap = new WeakHashMap<MyRef, Object>();
		MyRef a = new MyRef();
		MyRef b = new MyRef();
		weakmap.put(a, "aaa");
		weakmap.put(b, "bbb");
		weakmap.remove(a);

		a = null;
		b = null;
		System.gc();
		Iterator<Entry<MyRef, Object>> i = weakmap.entrySet().iterator();
		while (i.hasNext()) {
			Map.Entry<MyRef, Object> en = i.next();
			System.out.println("map:" + en.getKey() + ":" + en.getValue());
		}
	}

 

 

 

参考:

java GC and PhantomReference

Java引用总结

深入探讨 java.lang.ref 包

理解 Java 的 GC 与 幽灵引用

WeakHashMap和HashMap的区别

 

 

 

 

 

 

 

  • 大小: 19.7 KB
  • 大小: 38.7 KB
  • 大小: 57.4 KB
分享到:
评论

相关推荐

    java-正则表达式-分组引用介绍

    ### Java中的正则表达式:分组引用介绍 #### 概述 正则表达式是计算机科学中一种非常强大的文本处理工具,在Java等编程语言中广泛应用于字符串匹配、搜索替换等场景。当需要对正则表达式的部分结果进行进一步处理...

    mysql-connector-java-5.1.7.zip

    开发者在Java项目中引用这个JAR文件,就可以通过JDBC API与MySQL数据库进行通信。 2. **README.txt**:这是一个常见的文本文件,通常包含关于软件的安装指南、使用说明、版本信息或版权信息等。对于MySQL Connector/...

    最新版 JDBC,版本号为mysql-connector-java-8.0.16

    JDBC(Java Database Connectivity)是Java编程语言与各种数据库之间通信的一种标准接口。最新版的JDBC驱动程序,即mysql-connector-java-8.0.16,是MySQL官方提供的用于连接Java应用程序到MySQL数据库的工具。这个...

    taobao-sdk-java

    `taobao-sdk-java-20110512.jar` 是淘宝SDK的核心库文件,包含了所有必要的类和方法,供开发者在Java项目中引用。这个版本号(20110512)表示该SDK的发布日期,开发者需要根据实际需求选择合适版本的SDK以确保兼容性...

    javacv-platform-1.5.5-bin.zip

    1. **JavaCV介绍** JavaCV是基于Java的计算机视觉开发包,由Tangram Vision公司维护。它的目标是简化Java和Android上的计算机视觉应用开发,通过提供对多个跨平台库的简单接口,如OpenCV、FFmpeg、ImageIO、HighGUI...

    Java-Interview-超全集合github上评分最高的jiva面试题

    - **数据类型与变量**:了解Java的八种基本数据类型以及引用类型,掌握变量的声明、初始化和作用域。 - **流程控制**:包括条件语句(if, switch)和循环(for, while, do-while)的使用。 - **面向对象**:深入...

    java-内存-栈介绍

    存放方法栈、成员基本数据类型变量的引用和值、成员引用数据类型变量的引用

    jmeter连接MySQL数据库的驱动mysql-connector-java-5.1.38-bin

    本文将详细讲解如何利用JMeter连接MySQL数据库,并重点介绍使用"mysql-connector-java-5.1.38-bin.jar"驱动进行数据库连接的相关知识点。 首先,JMeter是一款强大的负载和性能测试框架,它可以对各种服务器、协议和...

    protobuf-java-3.11.2.zip

    - Java库文件:如protobuf-java.jar,供Java项目引用。 - 编译器工具:protoc,用于将.proto文件转换为Java代码。 - 示例和测试代码:展示如何使用protobuf API。 - 文档:介绍如何安装、使用和进一步了解...

    java-查找字符串介绍

    此外,Java正则表达式支持各种复杂的匹配规则,比如贪婪与非贪婪匹配、分组、反向引用等高级特性。这些特性的掌握对于编写更加灵活和精确的正则表达式非常有帮助。 #### 五、总结 本文详细介绍了如何使用Java中的`...

    sdk-java-getting-started-eclipse_java_GettingStarted_

    2. **Eclipse IDE介绍**:介绍Eclipse的基本界面,包括工作区、透视图、视图、编辑器等,并演示如何创建一个新的Java项目。 3. **Java编程基础**:涵盖基本的Java语法,如数据类型、变量、控制流语句、类和对象,...

    protobuf-java-3.17.0.zip

    "protobuf-3.17.0"可能包含了protobuf的库文件和其他资源,如jar包,供Java项目引用。 使用protobuf时,你需要注意以下几点: 1. **版本兼容**:确保你的protobuf编译器版本与protobuf库版本相匹配,以避免编译或...

    google-api-java-client-1.6.0-beta.zip

    谷歌API Java客户端库是一个强大的工具,它为开发者提供了与Google各种服务进行交互的能力,包括但不限于Google Drive、Google Calendar、Google Maps等。在这个1.6.0-Beta版本中,我们可以看到一系列的组件和资源,...

    java--全家桶课件

    "Java--全家桶课件"可能指的是一个全面的Java学习资源集合,涵盖了从基础到高级的各种主题。这个压缩包包含了两个部分,分别是“第1部分:Java基础编程.zip”和“第2部分:Java高级编程.zip”,下面我们将分别探讨这...

    introduction-to-java-programming-comprehensive-version-10th-edition

    - **变量和数据类型**:解释了Java中的各种数据类型,包括基本类型(如int、double)和引用类型(如String、数组)。 - **控制结构**:涵盖了条件语句(if-else)、循环语句(for、while)以及其他流程控制指令。 - ...

    java-电子书类 李兴华java教程

    接着,你会接触到Java的基础语法,如数据类型(包括基本类型和引用类型)、变量、运算符、流程控制语句(如if条件语句、for循环、while循环和switch-case语句)以及方法的定义和调用。 其次,深入理解面向对象编程...

    java面试java-interview-guide-master.zip

    - 自我介绍:如何准备和进行有效的自我介绍。 - 项目经验:阐述项目经验时应关注的关键点。 - 技能展示:如何展示自己的技能和解决问题的能力。 - 行业动态:关注Java领域的新技术和发展趋势。 这份面试指南...

    java-package与import机制

    为了解决这个问题,并帮助初学者更好地理解Java的`package`和`import`机制,本文将详细介绍这两个核心概念。 #### 二、Package与Import的基本概念 **1. Package的作用** - **组织代码:** `package`是Java中用来...

    Java-SCJP SL-275原版教材

    8. **内存管理**:探讨Java的垃圾回收机制,包括对象的生命周期、引用类型(强引用、软引用、弱引用、虚引用)以及如何避免内存泄漏。 9. **反射**:介绍Java反射机制,允许程序在运行时动态获取类的信息并操作类的...

Global site tag (gtag.js) - Google Analytics