开始今天的学习之前,先做个小总结
从10.24到现在,我已经学习了Java技术架构的划分、Java工作原理、Java环境配置、Java主类结构、关键字与标识符、变量与常量、运算符、流程控制、方法和方法重载、数组和Arrays类、哈希值与内存地址、==运算符与equals()、JVM工作原理、面向对象编程思想、类与对象关系、封装、权限修饰符、构造函数、this关键字、static关键字和变量的初始化方式。学习到很多新的细节知识,尤其是通过JVM工作原理把之前很多死记硬背的知识点彻底理解了~
今天的学习内容是垃圾回收机制
通过之前的学习,我们已经知道对象是如何被创建并初始化的, 那么如何清理对象呢?换句话说,我们已经知道静态变量随着JVM进程结束而销毁,局部变量随着方法调用结束而销毁,实例变量随着对象的清理而销毁,那么如何清理对象呢?这就要说到Java的垃圾回收机制。
一旦对象失去了“遥控器”,它就成了“可回收”的对象,垃圾回收器(GC)就会将这些对象识别为“垃圾”,当内存不足时就会自动回收这些“垃圾”以清理内存。但GC只能回收那些用new操作符创建的对象,如果某些对象不是通过new操作符在内存获取一块内存区域,这种对象可能不被GC所识别(比如Native方法调用的malloc()或者在创建对象时打开的某些文件资源)。为了应对这种情况,Java允许在类中覆盖Object类的finalize()的方法,先将这部分内存区域清理掉。该方法的工作原理为:一旦GC准备好释放某对象占用的内存空间,将首先调用其finalize()方法执行一些必要的清理工作,并且在下一次垃圾回收动作发生时,才会真正回收该对象占用的内存。
注意:如果JVM并未面临内存耗尽的情况,是不会浪费CPU资源去执行GC或finalize()方法的(垃圾回收线程)。为此,Java提供了System.gc()方法和Runtime.gc()方法,但这两个方法的作用只是提醒虚拟机:程序员希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行,而且具体什么时候进行是取决于具体的虚拟机的,不同的虚拟机有不同的对策。结论:程序员无法强制启动GC!!
示例程序:
public class Test28 {
public static void main(String[] args){
Test28 t1=new Test28();//t1引用了别的对象,这个对象变为可回收的
t1=new Test28();
Test28 t2=new Test28();//t2被赋值为null,这个对象变为可回收的
t2=null;
go();
System.gc();//强制启动GC
}
public static void go(){
Test28 t3=new Test28();//go()方法调用结束,t3会被销毁,这个对象变为可回收的
}
//Object类的方法,用来回收不是通过new分配内存的对象
protected void finalize(){
System.out.println("调用finalize()");
}
}
我们可以看到,程序中描述了对象变为“可回收”的三种情况,并且在最后调用了System.gc()强制启动GC。程序运行结果是finalize()方法被调用了三次,也就是说每个对象的回收都会先调用其finalize()方法,然后再使用GC回收该对象占用的内存。
面试题:常见的GC垃圾收集算法?
答:标记-清除算法、复制算法、标记-整理算法,分代收集算法
其中分代收集算法是最常用的算法,一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。