前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java引用:强、软、弱、虚

Java引用:强、软、弱、虚

作者头像
Yuyy
发布2022-09-21 09:47:15
2640
发布2022-09-21 09:47:15
举报
文章被收录于专栏:yuyy.info技术专栏

Java引用分为强引用、软引用、弱引用、虚引用

一、强引用

代码语言:javascript
复制
public class M {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("m.finalize");
        super.finalize();
    }
}

public static void main(String[] args) throws IOException {
        M m = new M();
        System.out.println(m);
        m=null;
        System.gc();
        System.out.println(m);
        System.in.read(); //为了阻塞main线程,让gc工作线程运行(试了下,不用这个也能gc,但后面还有代码的话,就不会立即gc了。说明调用System.gc();只是通知jvm执行gc,至于什么时候执行gc,还是得看jvm)
    }
  • 输出
代码语言:javascript
复制
com.yuyy.java.training.base.gc.bean.M@2ff4acd0
null
m.finalize

二、软引用

代码语言:javascript
复制
public static void main(String[] args) {

        java.lang.ref.SoftReference<byte[]> m=new java.lang.ref.SoftReference<>(new byte[1024*1024*10]);//m和sr是强引用关系,但sr和byte[]是弱引用关系
        System.out.println(m.get());
        System.gc();
        try {
            Thread.sleep(1_000);//为gc的工作线程提供cpu运行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(m.get());//堆空间够时,及时gc,软引用的对象也不会被回收
        byte[] bytes = new byte[1024 * 1024 * 11];//撑爆堆
        System.out.println(m.get());//堆空间不够了,执行gc,回收软引用的对象
    }
  • Jvm参数:-Xmx20m
  • 输出
代码语言:javascript
复制
[B@2ff4acd0
[B@2ff4acd0
null
  • 适用场景:缓存(现在有很多缓存框架,相比软引用,有着更多的策略)

三、弱引用

  1. 调用System.gc();
代码语言:javascript
复制
public static void main(String[] args) {
        java.lang.ref.WeakReference<M> weakRef=new java.lang.ref.WeakReference<M>(new M());
        System.out.println(weakRef.get());
        System.gc();
        System.out.println(weakRef.get());
    }
  • 输出
代码语言:javascript
复制
com.yuyy.java.training.base.gc.bean.M@2ff4acd0
null
m.finalize
  • 注意:我们刚调用System.gc();完,gc并未执行(因为finalize方法没执行),软引用的对象已经获取不到了,但还未被回收
  • 利用VisualVm手动gc(使用的是idea插件:VisualVM launcher),可以看到弱引用关联的对象被回收
代码语言:javascript
复制
public void test1() {
        java.lang.ref.WeakReference<byte[]> weakRef=new java.lang.ref.WeakReference<byte[]>(new byte[1024*1024*50]);
        System.out.println(weakRef.get());
        try {
            Thread.sleep(20_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(weakRef.get());
    }

输出

代码语言:javascript
复制
[B@16b3fc9e
null
  1. 弱引用关联的对象持有强引用
  2. 执行gc时,会回收所有只被弱引用关联的对象,如果该对象持有强引用,是无法回收的
代码语言:javascript
复制
public void test(){
        M[] ms = new M[1000];
        for (int i = 0; i < 1000; i++) {
            ms[i]=new M();
        }
        java.lang.ref.WeakReference<M[]> weakRef=new java.lang.ref.WeakReference<M[]>(ms);
        System.out.println(weakRef.get());
        System.gc();
        System.out.println(weakRef.get());
    }
  • 输出
代码语言:javascript
复制
[Lcom.yuyy.java.training.base.gc.bean.M;@16b3fc9e
[Lcom.yuyy.java.training.base.gc.bean.M;@16b3fc9e
  • 执行gc后,weakRef关联的M[]还在
  • 我们看看弱引用的实战,threadlocal的内部类的内部类Entry,就是继承的弱引用。更多关于ThreadLocal的知识,请看我的这篇文章

四、虚引用

gc时,就会回收,与此同时,会将被回收对象的引用添加到引用队列。

可对该引用队列进行监控,如果有引用进来了,说明有对象被回收了

类似钩子函数,起到通知的作用

收到通知,可以做一些事。很多框架(例如Netty)使用了NIO,其中有个功能叫做零拷贝:数据经过网卡,到内存里的数据缓冲区,jvm对其操作时,可直接操作堆外内存。就不用将数据拷贝到堆内。相应的,对象被回收时,需要将堆外内存里的相关数据进行清除。这个场景,就可以用虚引用。

  • 分配堆外内存
代码语言:javascript
复制
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
  • 在对象被回收时,清理相应的堆外内存,使用的就是PhantomReference的子类
代码语言:javascript
复制
public class Cleaner extends PhantomReference {
    private static final ReferenceQueue dummyQueue = new ReferenceQueue();


虚引用也称为幽灵引用或幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被垃圾收集器回收后收到一个系统通知。例如回收堆外内存


为什么不用finalize方法来执行上诉需求?

从Java源码Finalizer.class中得知:在源码中,执行finalize()方法是通过开启一个低优先级的线程来执行的,而finalize()方法在执行过程中的任何异常都会被catch,然后被忽略,因而无法保证finalize方法里的任务会被执行完。
由于执行finalize()的是一个低优先级的线程,既然是一个新的线程,虽然优先级低了点,但也是和垃圾收集器并发执行的,所以垃圾收集器没必要等这个低优先级的线程执行完才继续执行。也就是说,有可能会出现对象被回收之后,那个低优先级的线程才执行finalize()方法。



对于软引用和弱引用,当执行第一次垃圾回收时,就会将软引用或弱引用对象添加到其关联的引用队列中,然后其finalize函数才会被执行(如果没覆写则不会被执行);而对于虚引用,如果被引用对象没有覆写finalize方法,则是在第一垃圾回收将该类销毁之后,才会将虚拟引用对象添加到引用队列,如果被引用对象覆写了finalize方法,则是当执行完第二次垃圾回收之后,才会将虚引用对象添加到其关联的引用队列。


当执行第一次垃圾回收时,发现该对象具有finalize方法且没被执行过,因而这个对象不会被回收,并调用该对象的finalize()函数。当执行第二次垃圾回收时,发现该类虽然覆写了finalize方法,但已经执行过了,就可以直接将该类回收。以上是覆写了finalize函数的类的回收过程。对于没有覆写finalize函数的类或者已经执行过一次finalize函数的类,在垃圾回收时更简单,直接被回收即可。


public class PhantomReference {

    private static final List<Object> LIST = new LinkedList<>();

    private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        java.lang.ref.PhantomReference<M> phantomReference = new java.lang.ref.PhantomReference<>(new M(), QUEUE);
        System.out.println(phantomReference.get());
        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                try {
                    Thread.sleep(1_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(phantomReference.get());// 这行代码如果被注释,将不会打印下面的输出内容,目前没搞明白
            }
        }).start();
        new Thread(() -> {
            while (true) {
                Reference<? extends M> ref = QUEUE.poll();
                if (ref != null) {
                    System.out.println("虚引用关联的对象已被回收,可进行其他操作");
                }
            }
        }).start();
    }
}

输出

null
null
null
m.finalize
null
null
null
null
null
null
null
null
null
null
null
null
null
null
虚引用关联的对象已被回收,可进行其他操作
null
null
null
null
null
null
null
null
null
null
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
    at com.yuyy.java.training.base.gc.PhantomReference.lambda$main$0(PhantomReference.java:35)
    at com.yuyy.java.training.base.gc.PhantomReference$$Lambda$1/1198108795.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)


上面有一行注释:目前没搞懂。通过打印GC日志,easyGC分析,有无那行代码,GC的情况是一样的,对象应该是被回收了的,但虚引用的引用队列并未包含那个引用。监控引用队列的那个线程也是正常工作的,甚至在持续生产对象那个线程OOM时,监控线程也未受影响,一直正常运行着。

打印GC的jvm参数

-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-Xloggc:gc.log



				
				Post Views:
				343
				

	
	
	
	

	
						
				
					
				
			
			
				
					豆
				
			
			
				
					
				
			
			
				
					
				
			
			
				
					
				
			
				
			
				
			
		
		
			
				
			
		
		
			
				
			
		
				
			
				
			
		
	
	
		
	


	socialShare("#share", {
	    title : 'Java引用:强、软、弱、虚',
	    description : 'Java引用分为强引用、软引用、弱引用、虚引用 一、强引用 public class M { @Ov…',
	    wechatQrcodeTitle : "分享到微信",
	    wechatQrcodeHelper : '微信扫描二维码',
	    source : 'https://yuyy.info/?p=1295'
	});
	$("#share_show")[0].onclick = function(){
		$("#share_container").addClass("opened");
	};
	$("#share_copy_link")[0].onclick = function(){
		let input = document.createElement('input');
		document.body.appendChild(input);
		input.setAttribute("value", window.location.href);
		input.setAttribute("readonly", "readonly");
		input.setAttribute("style", "opacity: 0;mouse-events:none;");
		input.select();
		if (document.execCommand('copy')){
			iziToast.show({
				title: '链接已复制',
				message: "链接已复制到剪贴板",
				class: 'shadow',
				position: 'topRight',
				backgroundColor: '#2dce89',
				titleColor: '#ffffff',
				messageColor: '#ffffff',
				iconColor: '#ffffff',
				progressBarColor: '#ffffff',
				icon: 'fa fa-check',
				timeout: 5000
			});
		}else{
			iziToast.show({
				title: '复制失败',
				message: "请手动复制链接",
				class: 'shadow',
				position: 'topRight',
				backgroundColor: '#f5365c',
				titleColor: '#ffffff',
				messageColor: '#ffffff',
				iconColor: '#ffffff',
				progressBarColor: '#ffffff',
				icon: 'fa fa-close',
				timeout: 5000
			});
		}
		document.body.removeChild(input);
	};


	
					暂无评论
			




	
		
			
			发送评论
			编辑评论
		
		
			正在回复  的评论 :
			
			取消回复
		
		
			
				
					
				
				
					
				
			
						
				
					
						
							
								
							
							
						
					
				
				
					
						
							
								
							
							
						
					
				
				
					
						
							
								
							
							
							
								.post-comment-captcha-container:before{
									content: attr(captcha);
								}
							
													
					
				
			
			
				
					
						
							
								
							
							
						
					
				
			
			
				
					
						
					
				
			
				
											
							
							Markdown
						
																					
							
							邮件提醒
						
										
						
						
						发送
						编辑
					
					
						
						取消
					
											
							
						
						
	
		|´・ω・)ノヾ(≧∇≦*)ゝ(☆ω☆)(╯‵□′)╯︵┴─┴ ̄﹃ ̄(/ω\)∠( ᐛ 」∠)_(๑•̀ㅁ•́ฅ)→_→୧(๑•̀⌄•́๑)૭٩(ˊᗜˋ*)و(ノ°ο°)ノ(´இ皿இ`)⌇●﹏●⌇(ฅ´ω`ฅ)(╯°A°)╯︵○○○φ( ̄∇ ̄o)ヾ(´・ ・`。)ノ"( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃(ó﹏ò。)Σ(っ °Д °;)っ( ,,´・ω・)ノ"(´っω・`。)╮(╯▽╰)╭o(*////▽////*)q>﹏<( ๑´•ω•) "(ㆆᴗㆆ)😂😀😅😊🙂🙃😌😍😘😜😝😏😒🙄😳😡😔😫😱😭💩👻🙌🖕👍👫👬👭🌚🌝🙈💊😶🙏🍦🍉😣Source: github.com/k4yt3x/flowerhd	
	
		颜文字Emoji小恐龙花!	
									
			
			
			
		
	


	
		
			
				
				
					×
				
			
			
		
	


	
		
			
				
				
					×
				
			
			
			
				
				
			
		
	

上一篇利用脚本定时提交笔记下一篇 Java:ThreadLocal
					
						渝ICP备19004742						Theme Argon
					
				
			
		
		
						
					
		
	

<!--//--><![CDATA[//><!--
var cf = document.getElementById("commentform");
if(cf){
	var msghtml = "<strong>NOTICE:</strong> You should type some Chinese word (like “你好”) in your comment to pass the spam-check, thanks for your patience!";
	if(cf.insertAdjacentHTML){
		cf.insertAdjacentHTML("afterEnd", msghtml);
	}else{/* FireFox */
		var range = cf.ownerDocument.createRange();
		range.setStartAfter(cf);
		var frag = range.createContextualFragment(msghtml);
		cf.parentNode.insertBefore(frag, cf.nextSibling);
	}
}
//--><!]]>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-12-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java引用分为强引用、软引用、弱引用、虚引用
    • 一、强引用
      • 二、软引用
        • 三、弱引用
          • 四、虚引用
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档