前置问题讲解
什么是
finalize
方法?
finalize
方法是在对象被进行不可撤销的回收之前做的最后的处理,我们后续会重写该方法,目的是验证是否进行了垃圾回收.
finalize
在平时的工作中使用不到的,并且在JDK11
之后官方文档表明该方法存在问题,被废弃.
强引用
当内存不足时,JVM
开始进行垃圾回收,但是对于强引用的对象,就算出现了OOM
也不会对该对象进行回收.
强引用就是我们常见的普通对象的引用,只要还有强引用指向一个对象,就能表明该对象还"活着",垃圾收集器就不会碰这种现象.
在Java
中最常见的就是强引用,把一个对象赋值给另一个对象,这个引用变量就是一个强引用.
当一个对象被强引用变量引用时,它处于可达状态,不会被垃圾回收机制强制回收的,即使该对象之后永远不会被用到,JVM
也不会回收.
强引用的特性
- 不被回收:只要强引用存在,相关联的对象就不会被垃圾回收。
- 生命周期:强引用的生命周期与引用变量的生命周期相同;当引用变量超出作用域或被设为
null
时,强引用才会消失 ,从而 使对象可被回收。
强引用案例
class MyObject{
protected void finalize(){
//finalize通常的目的是在对象进行不可撤销的回收之前执行的清理操作
System.out.println("------------调用了该finalize方法!!");
}
-------------------------------------------------------------------------------------------------
public class Code05 {
public static void main(String[] args) {
MyObject myObject = new MyObject();//一般创建的对象都是强引用
System.out.println("gc回收之前: "+myObject);
//人为调用垃圾回收
System.gc();
System.out.println("未置空的状态下进行gc回收:"+myObject);
System.out.println("-------------------------------------------------");
myObject=null;//注意!!这里将myObject置空,表达的是将该线程栈帧上的myObject与堆断开连接
//说明堆中开辟的内存是不需要的了,这样才可以进行验证
//人为调用垃圾回收
System.gc();
try {
TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("置空的状态下进行gc回收: "+myObject);
}
}
}
运行方法:
说明:在第一次未置空的条件下,手动调用
gc
回收,发现并没有触发gc
回收(原因:没有调用finalize
方法且哈希址没有改变);而在第二次置空的条件下,我们手动调用gc
回收,发现此时才真正触发了gc
回收机制.说明**强引用只有在超出作用域,或者置空的条件下,才会被JVM
进行回收
软引用
在 Java
中,软引用(SoftReference
)是一种引用类型,它允许被引用的对象在内存不足时被垃圾回收,但在内存充足的情况下,软引用指向的对象不会被回收。软引用通常用于实现内存敏感的缓存机制。
软引用的特性
- 内存敏感:软引用的对象在
JVM
内存不足时会被回收,但在内存充足时不会被回收。 - 适用于缓存:软引用常用于实现缓存,因为它们可以在内存充足时保留对象,而在内存紧张时自动释放对象。
软引用案例
/*软引用:在内存充足时不会进行回收,内存不足时就会发生回收*/
public static void main(String[] args) {
MyObject myObject = new MyObject();
//用弱引用进行封装
SoftReference<MyObject> myObjectSoftReference = new SoftReference<MyObject>(myObject);
System.out.println("------内存充足时 :" +myObjectSoftReference.get());
myObject=null;
System.gc();
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("------内存充足时 :" +myObjectSoftReference.get());
}
手动调整JVM
:
/*软引用:在内存充足时不会进行回收,内存不足时就会发生回收*/
public static void main2(String[] args) {
MyObject myObject = new MyObject();
//用弱引用进行封装
SoftReference<MyObject> myObjectSoftReference = new SoftReference<MyObject>(myObject);
System.gc();//人工回收
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("------内存充足时 :" +myObjectSoftReference.get());
myObject=null;
try {
byte[] bytes=new byte[20*1024*1024];//20MB对象
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("------内存不充足时 :" +myObjectSoftReference.get());
}
}
运行结果
我们发现,这里发生了垃圾回收!!
但是,为什么我们在强引用中进行置空操作,就可以手动触发gc
回收,但是在软引用中单纯进行置空操作并不够,还需要模拟OOM
环境呢?
我们接下来进行讲解.
强引用和软引用之间的关系
在 Java
中,软引用(SoftReference
)的设计目的是为了在内存充足的情况下保留对象,即使该对象的强引用已经被置空。以下是一些关键点,解释了为什么软引用在强引用被置空的情况下依然不会立即触发垃圾回收:
-
软引用的特性
内存敏感性:软引用的对象只有在JVM
确定内存不足时才会被回收。这意味着即使强引用被置空,软引用指向的对象仍然可能被保留,直到系统需要更多的内存。垃圾回收的时机:垃圾回收器在运行时会检查内存使用情况,并决定是否需要回收软引用指向的对象。如果
JVM
认为当前可用内存足够,它不会立即回收软引用。 -
垃圾回收的流程
GC
触发:当JVM
进行垃圾回收时,它会检查所有的引用类型。对于软引用,只有在内存不足的情况下,才会将其指向的对象标记为可回收。 -
强引用与软引用的关系
即使强引用被置空,软引用仍然可以保持对象的存活,直到内存紧张。这是因为软引用的设计初衷是为了实现一种“柔性”的内存管理。
弱引用
弱引用需要用java.ref.WeakReference
类来实现,它比软引用的生存期更短.
对于只用弱引用的对象来说, 只要垃圾回收机制已运行,不管JVM
的内存空间是否足够,都会回收该内存.
弱引用的特性
- 可回收性:与强引用不同,只要没有强引用指向某个对象,当垃圾回收器运行时,弱引用指向的对象会被回收。
- 适用场景:常用于缓存实现、监听器等场景,以避免内存泄漏。
弱引用案例
public static void main(String[] args) {
MyObject myObject = new MyObject();
//用弱引用进行封装
WeakReference<MyObject> myObjectWeakReference = new WeakReference<MyObject>(myObject);
System.out.println("------gc before内存够用: "+myObjectWeakReference.get());
myObject=null;
System.gc();
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("-----gc after内存够用: "+myObjectWeakReference.get());
}
运行结果
虚引用
虚引用需要java.ref.PhantomReference
类来实现,顾名思义,就是形同虚设.与其他几种引用都不同,虚引用并不会决定对象的生命周期.如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可以被垃圾回收.
所以他不能用来单独使用,也不能通过它来访问对象,虚引用必须和引用队列(ReferenceQueue
)联合使用.
虚引用的特性
- 由于虚引用不能用来访问对象,所以
PhantomReference
的get
方法的返回值总是null
. - 虚引用的作用其实是跟踪对象被垃圾回收的状态.主要是为了帮助对象在被垃圾回收的时候进行一些额外的处理,而不是阻止对象被回收.
- 虚引用在创建时必须与一个引用队列(
ReferenceQueue
)相关联.当虚引用指向的对象被垃圾回收时,虚引用就会被加入到与之关联的引用队列中.这样做的目的是:为了能够在对象被垃圾回收时执行一些额外的操作,例如资源释放或者通知其他部分) - 用途:虚引用通常用于执行一些对象被回收时的清理工作.例如,可以使用虚引用来释放对象占用的资源,关闭文件或释放其他系统资源。通过监视与虚引用关联的引用队列,可以及时地执行这些清理或通知操作,确保系统资源得到有效释放,从而提高系统的性能和可靠性