博客起名为Java垃圾收集机制,给人的感觉就像是垃圾收集是Java语言特有的。事实上,垃圾收集(Garbage Collection)远比Java久远。垃圾收集需要考虑3件事情:哪些内存需要回收、什么时候回收、如何回收。带着这三个问题,我们去看看Java是如何实现垃圾回收的。

  Java的垃圾回收(GC)机制主要作用于运行时数据区的哪些部分呢?在上篇博客“Java虚拟机工作原理”中我们介绍了JVM运行时数据区有程序计数器、虚拟机栈、本地方法栈、堆、方法区5个区域。其中前三个区域随线程的创建而创建,随线程的消亡而消亡;栈中的栈帧随着方法的进入和退出而有条不紊地执行出栈和入栈操作。因此这三个区域的不需要过多的考虑垃圾回收问题。而Java堆和方法区则不一样一个接口的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的。因此垃圾收集器所关注的也就是这部分内存。

  回到垃圾收集的第一件事上:哪些内存需要回收?Java堆中存放着程序中几乎所有的对象实例,垃圾收集器在对堆进行回收前,首先需要判断哪些对象还“活着”,哪些已经“死去”。通常判断的方法有引用计数算法、可达性分析算法。引用计数算法给对象中添加一个引用计数器,每当一个地方引用它时,计数器值加1;当引用失效时,计数器值减1,如果计数器的值为0,则说明对象不再被使用(死去了)。然而Java虚拟机中并没有选用计数算法来管理内存,因为引用计数算法难以解决对象之间相互循环引用的问题。可达性分析算法是将一系列称为“GC Roots”的对象作为起始节点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的(也死去了)。其中可作为GC Roots对象的有:虚拟机栈(栈帧中本地变量表)中引用的对象,方法去中静态属性引用的对象,方法区中常量引用的对象,本地方法栈中引用的对象。上边说的都是Java堆中的内存回收,而方法区(HotSpot中的永久代)的垃圾收集主要回收两部分内容:废弃常量和无用类。判断一个常量是否是废弃常量只需判断是否还存在对该常量有引用的对象。而判断无用类需要同时满足3个条件:该类所有的实例都已经被回收,即Java堆中不存在该类的任何实例;加载该类的ClassLoader已经被回收;该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾收集算法

  • 标记-清除算法(Mark-Sweep算法)

  算法分为两个部分(标记、清除),首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。该算法主要有两个不足:一个是效率问题,标记和清除两个过程的效率都不高;一个是空间问题,标记清除后会产生大量的内存碎片。标记清除算法的执行过程如下图所示:

延伸阅读

学习是年轻人改变自己的最好方式-Java培训,做最负责任的教育,学习改变命运,软件学习,再就业,大学生如何就业,帮大学生找到好工作,lphotoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训学习是年轻人改变自己的最好方式