Java垃圾回收机制
简介
Java语言建立了垃圾回收机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。就是说,每隔一段时间java虚拟机会自动检测内存中是否存在没用的程序代码在占用内存,如果是的话就会把这些没用的或者使用过的内存中的代码清理掉,一般是说回收了垃圾。
该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。
垃圾回收算法的核心思想
对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。
触发GC(Garbage Collector)的条件
JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:
- 当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。
- Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。
由于是否进行GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其GC是反复进行的。
区分活动对象与垃圾的算法
- 引用计数法(Obj-C采用,Java不采用)
引用计数是垃圾收集的早期策略。在这种方法中,堆中每一个对象都有一个引用计数。当一个对象被创建了,并且指向该对象的引用被分配给一个变量,这个对象的引用计数被设置为1。比如新建一个对象A a=new A();然后a被分配给另外一个变量b,也就是b=a;那么对象a的引用计数+1。当任何其他变量被赋值为对这个对象的引用时,计数加1。当一个对象的引用超过生存期或者被设置一个新的值时,对象的引用计数减1,比如令b=c,则a的引用计数-1。任何引用计数为0的对象可以被当做垃圾收集。当一个对象被垃圾收集的时候,它引用的任何对象计数减1。在这种方法中,一个对象被垃圾收集后可能导致后续其他对象的垃圾收集行动。比如A a=new A();b=a;当b被垃圾回收以后,a的引用计数变为0,这样导致a也被垃圾回收。
方法的好处:引用计数收集器可以很快执行,交织在程序的运行之中。这个提醒对于程序不能被长时间打断的实时环境很有利。
方法的坏处:引用计数无法检测出循环(即两个或者更多的对象互相引用)。循环的例子如,父对象有一个子对象的引用,子对象又反过来引用父对象。这样对象用户都不可能计数为0,就算它们已经无法被执行程序的根对象触及。还有一个坏处就是,每次引用计数的增加或者减少都带来额外的开销。
- 追踪法(标记-清除法)
java从GC roots出发,遍历所有的引用,每当它找到一个存活对象,就会给对象一个标记。全部标记完成时,没有标记的对象将被释放
这种方法可以检测出循环引用,避免了引用计数法的缺点,JVM使用
JVM里面的GC Roots
GC roots:
- JAVA虚拟机栈中的本地变量引用对象
- 方法区中静态变量引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
原理:GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被GC所管理,因而选择这些区域内的对象作为GC roots,被GC roots引用的对象不被GC回收。
总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个:
1.给对象赋予了空值null,以下再没有调用过。
2.给对象赋予了新值,既重新分配了内存空间。
最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集。