介绍
java垃圾回收机制通过两种方法来判断对象是否回收,分别是引用计数法和可达性分析算法
java在jdk1.2之前,只存在一种引用,当对象只有被引用时才会存在,当没有引用时,引用计数为0,对象就会被垃圾回收判断为失效,进行回收
java在jdk1.2开始,对象的引用分为4种,分别是强引用>软引用>弱引用>虚引用,程序员可以通过不同的引用控制对象的生命周期,方便垃圾回收,使程序更加灵活的控制对象生命周期
java在jdk1.2开始,在java.lang.ref提供了一个抽象类Reference和三个实现类SoftReference(软引用)、WeakReference(弱引用)、PhantomReference(虚引用)
强引用
/**
* 强引用
*/
public void testStrongReference(){
//强引用
Object obj=new Object();
}
业务代码中的对象,绝大部分都是强引用,比如Object obj=new Object(),这种引用的对象绝对不会被垃圾回收,当内存不足以分配创建对象的内存时,Java虚拟机会抛出OOM错误,使程序异常终止,也不回收这种对象,只有这个引用消失后(* 显式赋值为null*,方法生命周期结束等),引用计数为0,才会被垃圾回收
软引用
/**
* 软引用
*/
public void testSoftReference(){
//强引用
Object obj=new Object();
//放入SoftReference
SoftReference<Object> softReference = new SoftReference<Object>(obj);
System.out.println( softReference.get());//java.lang.Object@39a054a5
}
软引用要比强引用低一个等级,用来表示有用非必要的对象,只有当内存不足的时候,才会对软引用对象进行垃圾回收,可以来实现缓存,不用担心存在内存溢出问题。
/**
* 软引用(引用队列)
*/
public void testSoftReferenceQueue(){
//引用队列
ReferenceQueue referenceQueue = new ReferenceQueue<>();
//使用守护线程
Thread thread= new Thread(new Runnable() {
@Override
public void run() {
try {
int i=0;
SoftReference<Integer> softReference;
//如果对象被gc回收
while((softReference = (SoftReference) referenceQueue.remove()) != null) {
System.out.println("第"+i+"次回收:"+softReference);
i++;
}
}catch (Exception e){
}
}
});
thread.setDaemon(true);//设置为守护线程,监听对象变化
thread.start();
Map<Object, Object> map = new HashMap<>();
Object obj=new Object();
for(int i=0;i<2000; i++){
//放入SoftReference转为软引用
byte [] bytes=new byte[1024*1024];
SoftReference<byte []> softReference = new SoftReference<byte []>(bytes,referenceQueue);
//将软引用作为key
map.put(softReference,obj);
}
}
第1992次回收:java.lang.ref.SoftReference@7c7a06ec
第1993次回收:java.lang.ref.SoftReference@1d296da
第1994次回收:java.lang.ref.SoftReference@776aec5c
[GC (Allocation Failure) [PSYoungGen: 30K->64K(1536K)] 3833K->3866K(5632K), 0.0002270 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 64K->64K(1536K)] 3866K->3866K(5632K), 0.0002042 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 64K->0K(1536K)] [ParOldGen: 3802K->3802K(4096K)] 3866K->3802K(5632K), [Metaspace: 3120K->3120K(1056768K)], 0.0019695 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 3802K->3802K(5632K), 0.0002220 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 3802K->730K(4096K)] 3802K->730K(5632K), [Metaspace: 3120K->3120K(1056768K)], 0.0025588 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第1995次回收:java.lang.ref.SoftReference@408d971b
第1996次回收:java.lang.ref.SoftReference@75d4a5c2
第1997次回收:java.lang.ref.SoftReference@557caf28
软引用通过SoftReference来进行实现,可以将软引用对象注册到引用队列(Referencequeue)中,引用队列来判断对象是否被释放,Reference存在next字段,有四种状态,可以根据状态判断,但next仅在放到queue中才会有意义( 因为,只有在enqueue的时候,会将next设置为下一个要处理的Reference对象 )
active:内存被分配的时候状态。
pending:即将回收内存,存入关联的引用queue中时的状态。
Enqueued:内存被回收的时候,进入引用队列中的状态。
Inactive:不活跃状态。
弱引用
/**
* 弱引用
*/
public void testWeakReference(){
//强引用
Object obj=new Object();
//放入WeakReference
WeakReference<Object> weakReference = new WeakReference<Object>(obj);
System.out.println(weakReference.get());//java.lang.Object@39a054a5
}
弱引用要比软引用第一个等级,用来表示非必要的对象,不管内存是否充足,都会垃圾回收其对象,但是因为垃圾回收线程优先级很低,所以不会很快回收
弱引用通过WeakReference来进行实现,可以将弱引用对象注册到引用队列(Referencequeue)中,引用队列来判断对象是否被释放。小伙伴们如果想测试,可以用上面弱引用的线程监听方法
虚引用
/**
* 虚引用
*/
public void testPhantomReference(){
//强引用
Object obj=new Object();
//必须使用引用队列
ReferenceQueue referenceQueue = new ReferenceQueue();
//放入PhantomReference
PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj,referenceQueue);
System.out.println(phantomReference.get());//null 因为虚引用无法获取对象实例,只是监听作用
System.out.println(obj);//java.lang.Object@39a054a5
}
虚引用
虚引用相较于其他引用,就如形同虚设,无法通过虚引用来获取对象实例,如果一个对象仅仅被虚引用持有,则随时会被垃圾回收,和软引用、弱引用不同,其必须注册到Referencequeue中,当对象即将被回收时,就会将虚引用放入到引用队列中,通过判断虚引用是否加入到引用队列来判断对象是否要被回收