垃圾回收机制
当程序创建对象数组等引用类型实体时,会在堆内存中创建一片区域用于存储。当其不存在引用时就成为垃圾等待垃圾回收机制将其回收。
特点:
- 只回收堆内存的对象,不会回收物理资源比如数据库连接网络IO等资源。
- 程序是不能精确控制垃圾回收的运行的,只有当对象永久性失去引用(不可达状态)时才会进行垃圾回收。
- 垃圾回收机制回收对象前首先会调用finalize方法,可能使对象复活导致垃圾回收机制取消回收。
当对象失去引用后,系统何时调用finalize方法进行资源清理 何时它会变成不可达对象 系统何时回收其所占内存 都不确定也不可以控制 程序能做的只有保证对象何时不会被引用 后面的活动无法控制
对象在内存中的状态:
-可达状态:对象在堆内存中,存在指向他的引用
-可恢复状态:没有任何指向它的引用,可以还经过回收时先调用的finalize方法对其进行资源-回收,在此阶段或许可以重新获取引用
-不可达状态:finalize方法也不能重新引用此对象,此时它已永久性的失去引用,进入此状态后等待系统回收。
当对象被其他类的类变量引用时只有该类被销毁,引用才消失;null
当对象被其他类的实例变量引用时,只有该对象销毁引用才消失。null
强制垃圾回收
程序无法准确控制java回收垃圾的时机但是可以通过调用System.gc()
静态方法和Runtime.getRuntime().gc()
实例方法进行强制垃圾回收,不过这种强制只是通知系统进行垃圾回收,具体何时做依然不确定,大部分时候会起到效果
使用java -verbose:gc 类名
命令执行程序可以看到每次垃圾回收时的提示信息包括内存占用变化。
finalize方法
protectedf void finalize() throws Throwable
此方法定义在Object类的实例方法中,垃圾回收机制回收某个对象前会首先要求程序清理资源,没有指定清理资源时Java提供此方法作为默认机制清理对象的资源。
当finalize方法返回后,对象消失,垃圾回收机制开始运行。
任何java类都可以重写finalize方法以自定义此对象如何清理资源。只有在垃圾回收机制认为需要更多额外内存时才会进行垃圾回收打算清理此对象时才会调用其finalize方法。所以说某个失去引用的对象占有少量内存而且系统也不需要大量内存
时就不hi进行垃圾回收清理资源,同样就不会调用finalize方法
特点
- 不要主动调用finalize方法,交给垃圾回收机制(
System.runFinalization`();``和
Runtime.getRuntime().runFinalzation();```) - finalize方法何时被调用具有不确定性(如果想清理某个类里打开的资源不要使用finalize方法)
- jvm执行可恢复对象的finalize方法时可能使使得对象或者系统中其他对象重新进入可达状态(看具体方法体中如何定义)
- jvm执行finalize方法出现异常时垃圾回收机制不会报告异常,程序继续执行
对象的软弱虚引用
java.lang.ref包下提供了三个类代表了系统对对象的三种引用方式SoftReference
、PhantomReference
和WeakReference
.
java对对象的引用方式
- 强引用(StrongReference)
常见的引用方式,将对象赋值给引用变量即可 - 软引用(SoftReference)
借助SoftReference类实现的引用,当对象只存在软引用时,当系统空间足够时不会被系统回收,但当系统内存空间不足时会被回收。 - 弱引用(WeakReference)
借助PhantomReference类实现的引用,当对象只存在弱引用时,就如同不可达对象一样,垃圾回收机制只要运行了,就随时会被回收。 - 虚引用(PhantomReference)
借助WeakReference类实现的引用,当对象只存在虚引用时,有它没它对于对象来说没有任何影响,就和不可达状态一样。他不能获取它引用的对象,(.get()方法返回null)主要用来跟踪对象被垃圾回收的状态。使用时必须配合引用队列(ReferenceQueue)使用
上述三个引用类都包含get()方法用以获取他们所对应的对象。
他们都是通过创建一个对象,借此对象来引用其他对象实现软弱虚引用的
引用队列由java.lang.ref.ReferenceQueue类表示,用于保存被回收会对象的引用。
当联合使用软引用、弱引用和引用队列时,系统回收被引用的对象后会将被回收对象对应的引用添加到关联的引用队列中。但虚引用有所不同,它在对象被释放之前就被添加到关联的引用队列中,这可以使得在对象回收之前采取行动。
软引用和弱引用可以单独使用但是虚引用不能,他的主要作用是跟踪对象被垃圾回收的状态,程序通过检查与虚引用关联的引用队列中是否包含了该虚引用,从而了解虚引用所引用的对象是否即将被回收。
public class Test{
public static void main(String[] args)
{
String str = new String("一个字符串");
//这里必须使用new而不是常量赋值,会被常量池管理(强引用),系统不会回收它
ReferenceQueue rq = new ReferenceQueue();
PhantomReference pr = new PhantomReference(srt,rq);
str=null;
//虚引用已经指向了str,str也只有一个虚引用
//此时打印pr.get()会得到null 因为虚引用无法得到被引用的对象
//执行垃圾回收
System.gc();
System.runFinalization();
//这时虚引用就会被放到引用队列中
此时rq.poll() 等于pr
}
}
使用引用类可以避免程序在执行期间将对象留在内存里。如果使用软弱
虚引用可以使得垃圾回收期随意释放对象。
注意:要使用这些引用类时必须保证不要有强引用同时存在,否则没有效果。
另外,当我们想从软弱引用中取出被引对象,var obj = wr.get()
有可能这个对象已经被回收了,需要使用的话只能重新创建。可以重新使用软弱引用引用对象wr = new WeakReferencr(recreateIt()); obj = wr.get()
或者重新创建对象并使用强引用引用它
obj = new recratIt(); wr = new WeakReference(obj);