在java中,我们并不需要同使用c/c++一样仔细的管理内存的分配和释放,因为java帮我们实现了内存的回收。垃圾回收(Garbage Collection ,GC)就是java来帮助我们管理内存的回收的。
具体而言,GC主要负责三项任务:分配内存、确保被引用对象的内存不被错误地回收以及回收不在被引用的对象的内存空间。
垃圾回收器的存在一方面把开发人员从释放内存的复杂工作中解脱了出来,提高了开发人员的效率;另一方面,GC屏蔽了释放内存的方法,避免了错误的操作内存而导致的应用程序的崩溃,保证了程序的稳定性。但是,垃圾回收也带来了一些问题,为了实现垃圾回收,必定会增加JVM的负担,降低了程序的执行效率。下面简单介绍一下GC的工作原理和使用GC过程中的注意事项。
GC回收的时机
对对象来说,如果没有任何变量去引用他,那么该对象将不可能被程序访问,因此可以认为他是垃圾信息,可以被回收。只有有一个以上的变量去引用该对象,该对象就不会被垃圾回收。
对于垃圾回收器来说,它使用有向图来记录和管理堆内存中所有的对象,通过这个有向图就可以是被哪些对象是“可达的”(有引用变量引用他就是“可达的”),哪些对象是“不可达的”(没有引用变量引用他就是不可达的),所有“不可达“的对象都是可以被垃圾回收的。可以参考实例如下:
public class Test{
public static main(String []agrs){
Integer i1 = new Integer(1);
Integer i2 = new Integer(2);
i2 = i1 ;
}
}
上述代码在执行到i2= i1后,内存引用关系如下图所示。
此时,如果垃圾回收器赈灾进行垃圾回收操作,在便利上述的有向图时,资源2所占的内存是不可达的,垃圾回收器就会认为这块内存已经不会再被使用了,因此就是回收该块内存空间。
垃圾回收算法
垃圾回收都是依据一定的算法进行的,下面介绍其中集中见长的垃圾回收算法。
引用计算算法(Reference Counting Collector)
引用计数作为一种简单但是效率较低的方法,主要原理如下:在堆中对每个对象都有一个引用计数器;当对象被引用时,引用计数器加1;当引用被置为空或离开作用域时,引用计数减1。由于这种方法无法解决相互引用的问题,因此JVM并没有采用这个算法。
追踪回收算法(Tracing Collector)
追踪回收算法利用JVM维护的对象引用图,从根节点开始便利对象的应用图,同时标记遍历到的对象。当遍历结束后,未被标记的对象就是目前已不被开始使用的对象,可以被回收了。
压缩回收算法(Compacting Collector)
把堆中的活动移动到堆中一端,这样就会在堆中另外一端留下很大的空闲区域,相当于对堆中的碎片进行了预处理。虽然这种方法可以大大简化消除堆碎片的工作,但是每次处理都会带来性能的损失。
负责回收算法(Coping Collector)
把堆分成两个大小相同的区域,在任何时刻,只有其中的一个区域被使用,知道这个区域的被消耗完为止,此时垃圾回收器会终端程序的执行,通过遍历的方式把所有活动的对象复制到另一个区域中,在复制的过程中他们时紧凑的,从而可以消除内存碎片。当复制过程后成勋会接着运行,知道这块区域被试用完。然后在采用上面的方法继续进行垃圾回收。
这个算法的有点时在进行垃圾回收的同时对对象的布置也进行了除了,从而消除了内存碎片,但是这个付出了很高的代价L对于指定大小的堆来说,需要两倍大小的内存空间,同时由于在内存调整的过程中要终端当前执行的程序,从而降低了程序的执行效率。
按代回收算法(Generational Colloector)
把堆分成两个或者多个子堆,每一个子堆被视为一代。算法在运行的过程中优先收集 那些”年幼“的对象,如果一个对象经过多次手机仍然时”存活“的,那么就可以把这个对象转移到高一级的堆里面。减少对其的扫描次数。
手动通知JVM进行垃圾回收
由于立即回收器的存在,java语言本身没有给开发者提供现实释放已分配内存的方法,也就是说,开发人员不能实时的调用垃圾回收器给某个对象进行垃圾回收。但是开发人员却可以通过调用System.gc()
方法来”通知“垃圾回收器运行。当然,JVM也并不会保证垃圾回收器马上就会运行,由于System.gc()
方法执行会停止所有的响应,去检查内存中是否有可回收的对象。这会对程序的正常运行已经性能造成极大的威胁,因此实际变成的过程中,不推荐频繁的使用这个方法。
当然,finalize()
也可以用于释放非 Java 资源,有点类似于C++中的析构函数。但是finalize()方法并一定每次都会调用。我们一般会通过他回收特殊渠道申请的内存。当然,这也得实在GC工作的情况下,如果机器的内存充足,或许永远不会调用到这个方法。
~ 对GC有一个简单的了解之后,我们对内存泄漏的分析和解决 就会比较简单了,下一篇将介绍啊java中的内存泄露和解决方案/。