在java中,处理基本数据类型意外,任何对象都是引用类型,不同的引用类型主要体现在对象是否可达和对垃圾收集器的影响。
java中的引用,可以分类为以下4种类型
- 强引用
- 软引用
- 弱引用
- 幻象引用(虚引用)
强引用
在日常程序中,我们通常使用=来给对象赋值,向Object obj = new Object();这种对象赋值方式就属于强引用,强引用存在期间(对象可达),当触发GC时,垃圾收集器不会触碰该类引用的对象。及时jvm内存不足,jvm会抛出OutOfMemoryError(内存溢出错误),也不会回收强引用类型的对象。但是需要注意的是,在方法、for循环等有作用域范围的强引用,如
void test(){
Object obj = new Object();
while(flag){
Object obj1 = new Object();
}
}
复制代码
在while中,obj1虽然是强引用类型,但是while结束后,就不可达了,所以obj1在while结束后就可以被gc回收了。
而obj的生命周期在整个test方法中,其引用保存与java栈中,而引用的真正内容保存在java堆中,当方法栈退出后,obj也就不可达了,其可以被GC回收,但是弱把OBJ赋值给一个外部的局部变量,引用又可达了,就不会被gc回收。
说的有点绕哈,就是强引用类型主要是看对象时候可达,若引用不可达,即没有一个对象指向对象的内存地址,那么gc就可以回收该空间。
软引用
前边说到,强引用及时JVM内存不足也不会在GC时回收对象内存空间,是否有一种引用类型,在jvm内存充裕时,引用存在,在内存不足时,gc回收空间呢?答案就是软引用,软引用是对强引用的弱化版。在内存充足时,引用可达,内存不足时候,回收对象地址。在JVM抛出OutOfMemoryError前,会查看堆中是否存在软引用类型的对象,弱存在,则回收该类型引用的空间,弱空间充裕了,就没必要抛出内存溢出的错误了。
软引用使用如下
//我使用了一个RefrenceTest的程序,重写finalize方法来判断gc时候回收
class RefrenceTest{
@Override
protected void finalize() throws Throwable {
System.out.println("finalize----");
}
}
//main方法如下
public static void main(String[] args) {
//构架一个强引用类型的数据
RefrenceTest refrenceTest = new RefrenceTest();
//构建一个软引用类型的数据
SoftReference<RefrenceTest> softReference = new SoftReference<>(refrenceTest);//
//使强引用不可达 就只剩下软引用了
refrenceTest = null;
//模拟内存不足的代码
java.util.List<Object> objectList = new ArrayList<>();
for (int i=0;i<10000000;i++){
objectList.add(new Object());
}
}
复制代码
当我new了100W个(90W个不回收,具体和jvm内存大小有关)强引用类型的Object对象放与ArrayList中时候,软引用被回收。
同时,软引用可以和引用队列(RefrenceQueue一起使用),当对象被gc前,对象地址会被先放入引用队列中,等待下一次gc回收,使用如下
public static void main(String[] args) {
//构架一个强引用类型的数据
RefrenceTest refrenceTest = new RefrenceTest();
// 引用队列
ReferenceQueue<RefrenceTest> referenceQueue = new ReferenceQueue<>();
//构建一个软引用类型的数据
SoftReference<RefrenceTest> softReference = new SoftReference<>(refrenceTest,referenceQueue);//
//使强引用不可达 就只剩下软引用了
refrenceTest = null;
//模拟内存不足的代码
java.util.List<Object> objectList = new ArrayList<>();
for (int i=0;i<1000000;i++){
objectList.add(new Object());
}
}
复制代码
弱引用
前边说到,软引用类型在gc时会考虑内存是否足够以确定要不要回收,而弱引用则是在gc时不用考虑内存是否足够,当gc时扫描到弱引用对象时候,会立刻回收。
public static void main(String[] args) {
//构架一个强引用类型的数据
RefrenceTest refrenceTest = new RefrenceTest();
//构建一个软引用类型的数据
WeakReference<RefrenceTest> softReference = new WeakReference<>(refrenceTest);
//使强引用不可达 就只剩下弱引用了
refrenceTest = null;
//手动触发gc
System.gc();
}
复制代码
对于弱引用,也可以使用引用队列,当gc扫描到弱引用类型数据时候,会将地址放入引用队列中,等待下一次gc回收。
public static void main(String[] args) {
//构架一个强引用类型的数据
RefrenceTest refrenceTest = new RefrenceTest();
//引用队列
ReferenceQueue<RefrenceTest> referenceQueue = new ReferenceQueue<>();
//构建一个软引用类型的数据
WeakReference<RefrenceTest> softReference = new WeakReference<>(refrenceTest,referenceQueue);
//使强引用不可达 就只剩下弱引用了
refrenceTest = null;
//手动触发gc
System.gc();
System.out.println(softReference.get());//null
System.out.println(referenceQueue.poll());//输出Refrence内存地址
}
复制代码
幻象引用(虚引用)
幻象引用是4中引用中最特殊的一种类型,其规定在构造方法中必须增加引用队列,若一个对象只有幻象引用,则他和没有引用一样,在任何时候都会被gc回收。而触发gc时候,就会将地址添加到引用队列中,因此,使用幻象引用,可以来判断对象是否即将被GC回收,Cleaner机制就是用到来幻象引用(挖个坑,下次说Cleaner机制),使用方法:
//引用队列
ReferenceQueue<RefrenceTest> referenceQueue = new ReferenceQueue<>();
//构建一个幻象引用类型的数据
PhantomReference<RefrenceTest> phantomReference
= new PhantomReference<>(new RefrenceTest(),referenceQueue);
复制代码
最后,文章中可能有错误,我水平有限(还是个学生(^-^))欢迎大致指正学习。