Java中的内存回收与内存泄漏的防治

前言

对于JVM(Java虚拟机)的GC(垃圾回收机制)来说,是否回收一个对象的标准是:是否还有引用变量指向该对象。只有有引用变量指向该对象,那么JVM就不会考虑去回收它。

而在学习Java的时候,一般都是会说:Java有一套完整的垃圾回收机制,程序员可以不需要考虑内存。但是在实际应用中,还是会出现“内存泄漏”的情况。

对象在内存中的状态

基本上可以将JVM中的对象引用理解为离散中学到的有向图。

将对象当做有向图的顶点,将引用关系当做有向图的有向边,有向边总是从引用端指向被引用的变量。在JVM中,Java的各种对象都是由各个线程创建的,所以可以将Java的线程对象作为有向图的起点。

如果按照上述的方式,对于某一个对象来说,始终有大于等于一条路径能够从起点指到它,那么它就不会被GC。当然,如果找不到一条路径能够指向它,那么它就可能会被回收(注意这里说的是可能,因为GC的回收机制是由一套相对复杂的算法来决定的,所有的对象并不是失去引用马上就会被回收)。

那么,是不是所有的对象,只要有引用指向它,它就不会被回收呢?但是是否定的,原因请继续往下看。

而一般来说,对于Java中的对象引用有4种方式:强引用、弱引用、软引用和虚引用。

关于这几种引用的概念,简单的说明如如下:

强引用

对于强引用来说,可能我们平时在写代码的时候,都是用的强引用,被强引用的Java对象时不会被垃圾回收机制给回收的。即使系统资源非常紧张,即使有些变量以后都不会用到,JVM也不会强制回收被强类型引用的变量,如果系统内存实在不够,程序会直接抛出OutOfMemory异常。

而我们在平常的开发中,使用的最多的也是这种,比如
1Person person =new Person();

软引用

软引用需要通过SoftReference来实现,当一个对象只具有SoftReference时,它可能会被垃圾回收机制给回收,但是对于SoftReference的对象来说,当系统资源足够时,它是不会被系统回收,程序可以使用该对象,但是当系统资源不够的时候,GC会回收它。

使用的方法一般如下:

SoftReference<Person>[] persons=new SoftReference[1000000]

弱引用

弱引用和软引用有些相似,区别在于 弱引用的生命周期更短,弱引用需要通过WeakReference来实现,对于只有弱引用对象,当GC运行时,无论系统资源是否足够,该对象都会被回收。(这里还是需要提醒一下,GC的运行不是实时的,不是说当该对象不再被引用变量引用的时候,GC会立即运行来回收它)

当然了,这样的设计是有一定的道理的,这样是为了更加好的解决系统资源。由于GC的不定时,所以可能会遇到空指针异常,所以在进行业务逻辑处理的时候,建议在开始的时候,判断下该对象是不是为空。

虚引用

虚引用通过PhantomReference类实现,它完全类似于没有引用。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独被使用,虚引用必须要和引用队列(ReferenceQueue)一起使用。

对象在堆中的几种状态

一个对象在堆中运行时,根据它在有向图中的状态可以分为3种:

可达状态

当一个对象被创建后,有一个以上的引用变量引用它,在有向图中可以从起始顶点导航到该对象,那么就处于可达状态。

可恢复状态

如果程序中某个对象不在有任何引用变量引用它,它将先进入可恢复状态,此时从有向图的顶点不能导航到该对象,在这个状态下,系统的垃圾回收机制准备回收该对象所占用的内存,在回收该对象之前,会调用可恢复状态的对象的finalize方法进行资源清理,如果在系统调用finalize方法重新让一个以上的引用引用该对象,那么这个对象就可以变成可达状态。否则,该对象将进入到不可达状态。

不可达状态

当对象的所有关联都被切断,且系统调用所有对象finalize依然没有使该对象变为可达状态,那这个对象将永久性的失去引用,当一个对象处于不可达状态时,系统才会真正的回收该对象所占用的资源。

Java的内存泄漏

定义:程序运行过程中会不断的分配内存空间,那些不再使用的内存空间应该立即回收它,从而保证系统再次使用这些内存,如果存在无用的内存被回收回来,那么就叫内存泄漏。

内存管理的技巧

1、尽量使用直接量,比如String s=:”hello”;而不是String s=new String (“hello”),这两种方法都会在再字符串缓冲池里面有缓存,区别是使用new的方式底层会创建一个char[]数组。

2、使用StringBuilder和StringBuffer进行字符串连接。如果使用String对象进行字符串连接,会生成大量的临时字符串会导致性能下降。

3、尽早释放无用对象的引用。

4、尽量少使用静态变量,因为静态变量的生命周期和类同步,只要class没有被卸载,那么就会一直常驻内存。

5、避免在经常调用的方法,循环中创建Java对象。这样会导致系统不断的为变量分配释放空间。

6、缓存经常使用的对象:如果有些对象需要被经常使用,那么可以考虑将这些对象用缓冲池保存起来,典型的缓存就是数据连接池。

PS:缓存的设计本身就是一种牺牲系统空间来换取运行时间的方式。如何控制缓存容器占用的内存空间不至于太大,同时又能保存大部分需要使用到的对象,这是缓存的设计的关键。

7、尽量不要使用finalize方法

8、考虑使用softReference,但是要注意软引用的不确定性。

总结

引用对象有4个级别,其由高到低一次为:强引用、软引用、弱引用和虚引用。在需要严格考虑系统资源的情况下,还是需要考虑到这个系统的消耗的,这个时候就不能一味的使用强类型引用了。

-------------The End-------------

本文标题:Java中的内存回收与内存泄漏的防治

文章作者:Dimple

发布时间:2018年05月31日 - 10:05

最后更新:2018年09月17日 - 20:09

原始链接:http://www.bianxiaofeng.com/2018/05/31/2018-5-31-10-00-19/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

na,给我一个棒棒糖!
0%