java对象四大引用案例

目录

目录

目录

0.前言:

1.强引用:

2.软引用:

3.弱引用:

4.虚引用:

5.面试题扩展:

6.线程池中的ThreadLocal:


0.前言:

1.强引用:当对象被一个或一个以上的引用类型变量引用时,此对象处于可达状态不会被回收。(当没有引用变量引用此对象时,对象就处于不可达状态。 就非常非常可能会被回收)

2.软引用:软引用需要用到SoftReference类,内存不够时,可能会被回收。

(通常用于内存敏感的程序中) 对于不是必须一直存在于内存中的对象可以使用软引用,比如处理一张图片的过程,如果更重要的程序需要在处理过程中途运行,可以将这张图片使用软引用。 (应用于缓存数据<硬盘中还有一份儿, 内存不够时可以清理这个缓存中的数据,因为硬盘中还有一份儿数据>)

(中途的程序在内存中存不下时,就删除这个软引用代表的对象)

3.弱引用:弱引用需要用到WeakReference类,无论内存够不够,只要垃圾回收器启动,弱引用关联的对象肯定被回收。用来描述非必需对象。(和软引用很像,但比软引用级别更低)。

常见的应用是ThreadLocal保存数据时key采用弱引用。

再比如: java集合中有一个WeakHashMap专门用来存储弱引用的(map中存储弱引用,弱引用指向一个对象)

WeakHashMap<String, Object> whm=new WeakHashMap<String, Object>();

4.虚引用:PhantomReference: 虚引用需要用到PhantomReference类, 用来在对象被回收时接收一个系统通知。

如果一个对象具有虚引用,那么它和没有任何引用一样,

被虚引用关联的对象引用通过get方法获取到的永远为null,

也就是说这种对象在任何时候都有可能被垃圾回收器回收,

通过这种方式关联的对象也无法调用对象中的方法。

虚引用主要是用来管理堆外内存的,通过ReferenceQueue这个类实现,当一个对象被回收的时候,会向这个引用队列里面添加相关数据,给一个通知。

设置虚引用的唯一目的,就是在这个对象被回收器回收的时候收到一个系统通知或者后续添加进一步的处理。

1.强引用:

最常见的一种对象引用方式:  Test1 myObject = new Test1();

/**
 * 
 * @author zhaoYQ
 * 
 *
 */
public class Test1 {
	@Test
	public void strongReference() throws InterruptedException {
		 Test1 myObject = new Test1();
	     System.out.println("Gc前:" + myObject);
	 
	     System.gc();
	         TimeUnit.SECONDS.sleep(1);
	     //强引用:  未置空无法被GC
	     System.out.println("未置空Gc后打印:" + myObject);
	 
	     myObject = null; //置空可被GC
	     System.gc();
	     System.runFinalization();//强制调用finalize
	     TimeUnit.SECONDS.sleep(1);
	     System.out.println("置空后Gc后打印:" + myObject);//置空可被GC

	}

	
	protected void finalize() throws Throwable {
		System.out.println("对象被回收");
		super.finalize();
	}

}

2.软引用:

package com;

import java.lang.ref.SoftReference;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
/**
 * 
 * @author zhaoYQ
 *  软引用
 *
 */
public class Test1 {
	@Test
	public void strongReference() throws InterruptedException {
		 byte[] t1 = new byte[1024*1024*10];//10M
		 SoftReference<byte[]> softReference = new SoftReference<byte[]>(t1);
	     System.out.println("Gc前,打印对象:" + softReference.get());
	 
	     
	     t1=null;
	     System.gc();//将t1的引用置空//只存在软引用(不存在强引用)
	     TimeUnit.SECONDS.sleep(1);
	     System.out.println("Gc后,打印对象:" + softReference.get());
	     
	     //再次创建一个数组,堆中存不下的时候,垃圾回收器工作
	     //先回收一次,如果第一次回收后内存还是不够
	     //则再清理第二次,这一次会把软引用对象清除
	     byte[] t2 = new byte[1024*1024*15];//15M
	     System.gc();//启动GC(但是GC不一定会回收某个对象<要看当时堆内存环境>)
	     TimeUnit.SECONDS.sleep(1);
	     System.out.println("Gc后,打印对象:" + softReference.get());
	}


}

运行时设置堆内存大小(设置运行参数):

-Xmx100M 当堆最大内存设置为100M时因为内存够用,所以出发GC时,对象不会被回收。

如下,当当堆最大内存设置为25M时因为内存不够用, 所以当需要往堆中再次分配一个更大的t2时,就要回收t1了(所以软引用是当内存不够时才可能回收<内存空间够用时不会回收>)

3.弱引用:

package com;

import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
/*
 System.gc();    等效于: Runtime.getRuntime().gc()
//告诉垃圾收集器打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的

System.runFinalization();  等效于:    Runtime.getRuntime().runFinalization()
//强制调用已经失去引用的对象的finalize方法 
 */
/**
 * 
 * @author zhaoYQ
 * 
 *
 */
public class Test1 {
	@Test
	public void strongReference() throws InterruptedException {
		// 1.创建一个对象
		Test1 t1 = new Test1();// 强引用,未置空无法被GC
		// 2.创建一个弱引用,让弱引用引用对象t1
		WeakReference<Test1> wr = new WeakReference<Test1>(t1);
		// 3.切断t1和对象之间的引用
		t1 = null;
		// 4.取出弱引用所指代的对象
		System.out.println("gc前打印对象:"+wr.get());//打印对象

		// 5.催促垃圾回收
		System.gc();
		TimeUnit.SECONDS.sleep(2);
		System.runFinalization();//强制调用finalize

		// 6.再次取出弱引用所指向的对象
		System.out.println("gc后打印对象:"+wr.get());//null

	}

	
	protected void finalize() throws Throwable {
		System.out.println("对象被回收");
		super.finalize();
	}

}

执行结果如下:

gc前打印对象:com.Test1@fbb2c1

对象被回收

gc后打印对象:null

弱引用说明案例2:

package com.zyq.csmall.business.config;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
public class Test {
    // 运行结果:
    //  weakRef = com.zyq.csmall.business.config.Test$1M@19f7e29
    //  M对象被回收
    //  weakRef = null
    public static void main(String[] args) throws InterruptedException {
            class M{
                protected void finalize() throws Throwable {
                    System.out.println("M对象被回收");
                }
            }
            WeakReference weakRef=new WeakReference(new M());
            System.out.println("weakRef = " + weakRef.get());
            System.gc();
            TimeUnit.SECONDS.sleep(1);
            System.out.println("weakRef = " + weakRef.get());
    }
}

4.虚引用:

使用的情景非常少,不用掌握。

虚引用需要用到PhantomReference类, 用来在对象被回收时接收一个系统通知。

如果一个对象具有虚引用,那么它和没有任何引用一样,

被虚引用关联的对象引用通过get方法获取到的永远为null,

也就是说这种对象在任何时候都有可能被垃圾回收器回收,

通过这种方式关联的对象也无法调用对象中的方法。

虚引用主要是用来管理堆外内存的,通过ReferenceQueue这个类实现,当一个对象被回收的时候,会向这个引用队列里面添加相关数据,给一个通知。

设置虚引用的唯一目的,就是在这个对象被回收器回收的时候收到一个系统通知或者后续添加进一步的处理。

下边的情况下: 当堆中的对象被回收时,用来通知垃圾回收器将堆外的这个对象同时回收掉。

虚引用这这种情景下: 就可以在堆中的对象被回收时通知 垃圾回收器去回收堆外的这个字节缓冲数组 (因为堆中使用字节缓冲数组的对象已经要被回收了, 堆外的这个字节缓冲数组就没有存在的必要了)

案例: 作者用意没看出来

package com;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class Test {//这个给List中不停存入数组的情况体现不出虚引用,这个案例作者的用意没体会出来
	//-verbose:gc -XX:+PrintGCDetails -XX:+DisableExplicitGC  -XX:MaxDirectMemorySize=40M
	//                                     禁止手动调用System.GC()   堆外最大内存是40M
	
	private static final List<Object>  LIST=new LinkedList<>();
	private static final ReferenceQueue<Test>  QUEUE=new ReferenceQueue<>();
	public static void main(String[] args) {
		//虚引用使用的时候里边必须给引用的对象对应一个QUEUE的队列
		//用来在对象被回收之前(注意是回收之前),将把此对象对应的虚引用
		//添加到它关联的引用队列QUEUE中(用来告知虚拟机此对象关联的堆外对象也应该被回收掉)
		PhantomReference<Test>  phantomRef=new PhantomReference<>(new Test(),QUEUE);
		
		//注意: 虚引用和没有引用一样(使用虚引用无法获取所引用的对象)
		System.out.println(phantomRef.get());//null


		//ByteBuffer.allocate(1024)可以在堆外创建一个字节数组用来:
		//在nio通信中直接接收网卡的数据(如果不在堆外创建此数组,则需要
		//在堆中创建一个类似的数组来拷贝操作系统接收网卡后在堆外构建的
		//一个字节数组的数据)  (用ByteBuffer.allocate(1024)可以在堆外
		//创建一个字节数组用来直接接收网卡的数据<尤其在游戏编程中经常使用>)
		ByteBuffer b=ByteBuffer.allocate(1024);
		
		Thread t1=new Thread(()->{
			while(true){
				try {
					//往LIST集合每次存入一个大小为1M的数组(LIST集合会越来越大)
					LIST.add(new byte[1*1024*1024]);
					TimeUnit.SECONDS.sleep(1);
					//注意: 虚引用和没有引用一样(使用虚引用无法获取所引用的对象)
					System.out.println(phantomRef.get());//null
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		
		t1.start();
		
	}

}

5.面试题扩展:

你使用过ThreadLocal吗, ThreadLocal是否有可能会导致内存泄漏?

答案: A.ThreadLocal是某个线程给ThreadLocal中存入数据时,这个数据只能给这个线程用

(其他线程无法获取此数据) //放入的数据时引用类型的(对象)

B.ThreadLocal的数据使用完一定要调用ThreadLocal对象的remove方法将放入的数据

置为null, 这样垃圾回收器才会回收这个数据对象

案例扩展: ThreadLocal的使用方式如下:

package com.zyq.csmall.business.config;
import java.util.concurrent.TimeUnit;
public class TestTL {
    /**
     * ThreadLocal中有一个Map集合,可以给里边放入元素
     * (特点是: 哪个线程给ThreadLocal放元素,哪个线程才能获取这个数值
     *   <其他线程获取不到>)
     * 观察源码:ThreadLocal的set(T value)方法可以发现它从当前线程中会获取一个
     * Map,然后给里边放入元素:
     *      Thread t = Thread.currentThread();
     *      ThreadLocalMap map = getMap(t);
     *
     *          getMap(Thread t) {  return t.threadLocals;  }
     *      if (map != null)   map.set(this, value);
     *      //当前线程中有一个ThreadLocalMap(就是一个Map),把当前ThreadLocal     
     *      的引用(ThreadLocal本身)作为key,数值作为value放入到ThreadLocalMap
     *     //所以如果Map中需要放入多个value,就得有多个key(多个ThreadLocal)
     *
     *     注意: 
     *     观察ThreadLocal的set(T value)方法中的map.set(this, value)源码,
     *     发现:
     *      ThreadLocalMap是ThreadLocal的静态内部类
     *      这个ThreadLocalMap的set方法放元素逻辑是: 
     *      // map.set(this, value);
     *          ThreadLocalMap中封装了一个Entry数组,Entry键值对类继承了弱引用
     *          WeakReference类
     *          (键值对类在创建对象时,会先创建父类WeakReference对象: 
     *          super(k)父类对象会用key指向)
     *              super(k)(所以ThreadLocalMap的每个元素的
     *              key<ThreadLocal对象还会被一个弱引用指向ThreadLocalMap对象)
     *              (ThreadLocal对象会被tL强引用指向, 还会被一个弱引用
     *               WeakReference指向)
     *
     */
    public static void main(String[] args) {
        ThreadLocal<Integer> tL=new ThreadLocal<>();
        //ThreadLocal<Integer> tl2=new ThreadLocal<>();
        Thread th1=new Thread(){
            public void run() {
                tL.set(1);
                //tl2.set(2);
                System.out.println("Integer:"+tL.get());
                //System.out.println("Integer:"+tl2.get());
      //不用ThreadLocal之后一定要调用tL.remove()将Entry的value置为null
      //(弱引用解决key, tL.remove()解决value)
      //(弱引用指向的ThreadLocal对象在垃圾回收时会被回收掉, tL.remove会将
      // Entry的value置为null<将value指向的对象设置为垃圾对象, GC会回收此对象>)
      //否则会导致th1指向线程对象,线程对象指向ThreadLocalMap  threadLocals
      // threadLocals指向ThreadLoalMap对象,ThreadLoalMap对象指向Entry数组,
                tL.remove();
            }
        };
        th1.start();
        Thread th2=new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println("Integer:"+tL.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        th2.start();
    }
}


6.线程池中的ThreadLocal:

扩展线程池的TheadLocal的remove()问题:

同样的 ,在线程池中使用ThreadLocal存储数据时,每次将线程使用完也要将此线程的ThreadLocalMap清空,否则下一条线程可能会获取上一条线程的数据,也会导致内存泄漏。

参考文章:

(266条消息) Java四大引用_不会叫的狼的博客-CSDN博客_java的四大引用

https://blog.csdn.net/weixin_40017062/article/details/124958000

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 有四种不同类型的引用: 1. 强引用(Strong Reference):通常使用的引用方式,使用强引用对象在内存空间不足时不会被回收。 2. 软引用(Soft Reference):当内存空间不足时,对象可能会被回收,但是在回收之前它可以被缓存。 3. 弱引用(Weak Reference):当内存空间不足时,对象很快就会被回收。 4. 虚引用(Phantom Reference):它不能单独使用,必须和引用队列(ReferenceQueue)一起使用,当对象被回收时,JVM会把它加入到引用队列中。 这四种引用的不同在于,它们对垃圾回收器的回收策略不同,因此它们对于内存管理的影响不同。 ### 回答2: Java四大引用分别是强引用、软引用、弱引用和虚引用。 1. 强引用Java中最常见的引用类型。通过 `new` 关键字生成的对象默认都是强引用。强引用对象不会被垃圾回收器回收,即使内存不足时也不会被回收。只有当强引用失去引用时,垃圾回收器才会将其回收。 2. 软引用Java中的软引用是用来描述一些还有用但不是必需的对象。在内存不足时,垃圾回收器会选择性地回收软引用对象。这种引用常常用于缓存数据,当内存足够时,可以提供数据的快速访问,当内存不足时,可以被回收释放内存。 3. 弱引用Java中的弱引用是比软引用更弱一些的引用类型。弱引用对象在垃圾回收时,只要被垃圾回收器发现,就会被回收。弱引用通常用于跟踪对象的状态,当对象被回收时,可以执行特定的操作。 4. 虚引用Java中的虚引用是最弱的一种引用。虚引用主要用于对象回收跟踪。与弱引用不同的是,虚引用在任何时候都可能被垃圾回收器回收,甚至无法通过虚引用获取对象。虚引用一般与引用队列(Reference Queue)联合使用,用于监控对象被垃圾回收的状态。 引用的不同类型在内存管理中起到不同的作用,能够提供更灵活的内存控制机制。程序员可以根据对象的生命周期和内存需求,选择合适的引用类型,从而更好地管理内存。 ### 回答3: Java四大引用是强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。 1. 强引用是默认的引用类型,也是最常用的引用类型。当对象被一个强引用引用时,对象将会一直存在,不会被垃圾回收器回收。 2. 软引用用来描述一些还有用但并非必须的对象。当内存不足时,垃圾回收器可能会回收软引用对象所占用的内存。可以通过SoftReference类来创建软引用。 3. 弱引用用来描述非必须的对象,它的生命周期比软引用更短。当垃圾回收器执行垃圾回收时,无论内存是否足够,都会回收弱引用对象。可以通过WeakReference类来创建弱引用。 4. 虚引用是最弱的引用类型,几乎没有实质性的作用。用来跟踪对象被垃圾回收器回收的活动。无法通过虚引用获得对象的实例,其主要作用是在对象被回收前收到一个系统通知。可以通过PhantomReference类来创建虚引用。 在Java中,使用合适的引用类型可以更好地管理内存,避免内存溢出。强引用应该谨慎使用,避免产生内存泄漏。而软引用和弱引用则可以用来优化缓存机制,当内存紧张时自动释放缓存。虚引用则主要用于追踪对象的回收情况。理解并正确使用这四大引用可以提高代码的性能和健壮性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值