欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android面试题:Java垃圾回收机制和finalize()

程序员文章站 2022-12-11 09:09:30
android面试题:java垃圾回收机制和finalize()。 1. 垃圾回收的意义   在c++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象;而在java中...

android面试题:java垃圾回收机制和finalize()。

1. 垃圾回收的意义

  在c++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象;而在java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾。jvm的一个级线程会自动释放该内存块。垃圾回收意味着程序不再需要的对象是”无用信息”,这些信息将被丢弃。当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。事实上,除了释放没用的对象,垃圾回收也可以清除内存记录碎片。由于创建对象和垃圾回收器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,jvm将整理出的内存分配给新的对象。
  垃圾回收能自动释放内存空间,减轻的负担。这使java 具有一些优点。首先,它能使编程效率提高。在没有垃圾回收机制的时候,可能要花许多时间来解决一个难懂的存储器问题。在用java语言编程的时候,靠垃圾回收机制可大大缩短时间。其次是它保护程序的完整性, 垃圾回收是java语言安全性策略的一个重要部份。
  垃圾回收的一个潜在的缺点是它的开销影响程序性能。java虚拟机必须追踪运行程序中有用的对象,而且最终释放没用的对象。这一个过程需要花费处理器的时间。其次垃圾回收算法的不完备性,早先采用的某些垃圾回收算法就不能保证100%收集到所有的废弃内存。当然随着垃圾回收算法的不断改进以及软硬件运行效率的不断提升,这些问题都可以迎刃而解。

2. 垃圾收集的算法分析

java语言规范没有明确地说明jvm使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:
(1)发现无用信息对象;
(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用。

判断这两点比较常见的方法:引用计数法与可达性分析算法。

2.1 引用计数法(reference counting collector)

该方法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1,当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。
引用记数法逻辑比较简单,运行速度也比较快。但是它有一个致命的缺点,无法解决循环引用问题。
例如,a引用b,b也引用a,它们处于互相引用的状态。

a a = new a(); 
b b = new b();
a.ref = b;
b.ref = a;

此时,a和b各被引用两次。

a = null
b = null

这时我不再想使用这两个对象,将变量引用置为null,此时,a和b的引用计数各减1。但是,它们还是各被引用了1次,所以垃圾回收器无法回收这两个对象。

2.2 可达性分析算法

在主流的商用程序语言中(java和c#),都是使用可达性分析算法判断对象是否存活的。这个算法的基本思路就是通过一系列名为gc roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(reference chain),当一个对象到gc roots没有任何引用链相连时,则证明此对象是不可用的,下图对象object5, object6, object7虽然有互相判断,但它们到gc roots是不可达的,所以它们将会判定为是可回收对象。
Android面试题:Java垃圾回收机制和finalize()

那么那些点可以作为gc roots呢?一般来说,以下情况的对象可以作为gc roots:
- 虚拟机栈(栈桢中的本地变量表)中的引用的对象
- 方法区中的类静态属性引用的对象
- 方法区中的常量引用的对象
- 本地方法栈中jni(native方法)的引用的对象

3. finalize方法

finalize是在回收一个对象之前会先调用finalize方法。但是这个调用时机是不确定的,并且在不同的jvm中调用时机也是不确定的,最后,它可能还不会被执行!所以,我们并不能依赖finalize方法做一些事情。

但是,finalize有什么用呢?
释放非java代码创建的存储空间

这种情况出现在使用“本地方法的时候(jni中)”,例如你可能调用c的malloc()函数来分配存储空间,而且除非调用了free()函数,否则无法释放该存储空间,造成内存泄漏。

在这种时候就可以在finalize方法中再次调用本地方法释放,虽然finalize调用的时机不可预料或者不被调用,但总比不释放好。