对象可达性与内存泄漏

在Java中,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),JVM会在堆(Heap)中给每一个对象分配空间。

对象的内存释放是由GC完成的,GC为了能够正确释放对象内存空间,必须监控每一个对象的运行状态,当某一对象不再使用,即失去引用时,就可以将该对象的空间进行释放。

对象在内存中的状态

Java对对象的状态监控是采用的可达性分析算法,即把JVM内存中的对象引用理解成一种有向图,把引用变量、对象都当成有向图的顶点,将引用关系当成图的有向边,有向边总是从引用端指向被引用的对象。因为Java的所有对象都是由一条条线程创建出来的,因此可以把线程对象当成有向图的起始顶点。

对于单线程程序而言,整个程序只有一条main线程,那么该图就是以main进程为起始顶点的有向图。
在这里插入图片描述
在这个有向图中,main顶点可达的对象都处于可达状态,例如Obj1,垃圾回收机制将不会回收它们;如果某个对象在这个有向图中处于不可达状态例如Obj2,那么就认为这个对象不再被引用,垃圾回收机制将会回收它。

当一个对象在堆内存中运行时,根据它在对应有向图中的状态,可以把它所处的状态分成如下三种:

可达状态:当一个对象被创建后,有一个以上的引用变量引用它。在有向图中可以从起始顶点导航到该对象,那么它就处于可达状态,程序可以通过引用变量来调用该对象的属性和方法。

可恢复状态:如果程序中某个对象不再有任何引用变量引用它,它将进入可恢复状态,此时从有向图的起始顶点不能导航到该对象。在这种状态下,符合垃圾回收标准,在回收该对象之前,系统会调用finalize()方法进行资源清理,如果系统调用finalize()方法重新让一个以上的引用变量引用该对象,则这个对象将重新变为可达状态;否则该对象将进入不可达状态。

不可达状态:当没有引用变量引用对象,且系统调用finalize()方法依然没有使对象变成可达状态后,这个对象将永久性地失去引用,变成不可达状态,等待垃圾回收器回收。
在这里插入图片描述

强引用、软引用、弱引用和虚引用

强引用:

这是Java程序中最常见的引用方式,程序创建一个对象,并赋给一个引用变量,这个引用变量就是强引用。

前面讲的引用都是指强引用,当一个对象有一个或多个强引用时,Java垃圾收集器不会回收它。

软引用:

软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它有可能被垃圾回收机制回收–当内存空间充足时,它不会被回收;当内存空间不足时,它将会被垃圾回收机制回收。软引用通常用于对内存敏感的项目中,在此类项目中,软引用是强引用很好的替代者。

弱引用:

弱引用与软引用类似,区别在于弱引用引用的对象生存期更短,弱引用通过WeakReference类实现。

对于只有弱引用的对象而言,当垃圾回收机制运行时,不管内存是否足够,总是会被回收。当然,并不是说当一个对象只有弱引用时它就会被回收,也必须等到垃圾回收机制运行时才会被回收。

虚引用:

虚引用通过PhantomReference类实现,它完全类似于没有引用,虚引用主要用来跟踪对象被垃圾回收的状态,可以通过检查与虚引用关联的引用队列中是否已经包含指定的虚引用,从而来判断虚引用引用的对象是否将被回收。

了解了强引用、软引用、弱引用和虚引用之后,我们就可以将之前提到的对象状态转换图进行拓展了。
在这里插入图片描述

内存泄漏

什么是Java中的内存泄漏?

程序运行过程中会不断地为对象分配内存空间,那些不再使用的对象的内存空间应当及时被回收,从而保证这些内存空间可以再次被利用,如果存在无用的内存没有被回收回来,那就是内存泄漏。

造成内存泄漏的对象有两个特点:(1)对象是可达的,即具有强引用指向该对象;(2)对象是无用的,即程序不再需要引用这些对象。如果有对象满足这两个条件,那么这些对象就可以判定为Java中的内存泄漏。

引起内存泄漏的原因:

忘记释放先前分配的内存,就可能会造成内存泄漏,如果程序保留永远不再使用的对象的引用,这些对象将会占用并耗尽内存。为了能确保垃圾回收机制能够回收无用对象的内存空间,通常将对象的引用设置为null或者从集合中移除。

当局部引用变量不再使用时,不需要显示的设置为null,因为这些变量将随着方法的退出而自动清除。

(1)全局集合或数组

如果我们将对象存放在全局的集合或数组中,当对象不再使用时,不仅要将对象的引用设置为null,还要将对象从集合或数组中移除。

例如:

Book book0 = new Book();
		Book book1 = new Book();
		Book book2 = new Book();
		Book book3 = new Book();
		
		ArrayList<Book> bookList = new  ArrayList<Book>();
		bookList.add(book0);
		bookList.add(book1);
		bookList.add(book2);
		bookList.add(book3);
		
		book0 = null;
		book1 = null;
		book2 = null;
		book3 = null;
		
		bookList = null;

(2)缓存

缓存是一种数据结构,用于快速查找已经执行的操作结果。如果一个操作执行起来很慢,并且需要经常使用执行结果,我们就可以将执行结果缓存,在下次需要调用该操作时,直接使用缓存的数据。

如果该操作的执行有很多种不同的结果,那么也可能会造成内存泄漏,解决办法是,给缓存设定一个空间大小,当缓存达到限制的空间大小时,移除缓存中最久的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值