堆中存放了几乎所有的对象实例,垃圾收集器对堆进行回收时,第一件事就是判断那些对象已死,那些对象还活着。
判断对象是否活着有以下几种方法:
1.引用计数算法(Reference Counting)
给对象一个引用计数器,每当一个地方引用它时就加1,当引用失效时,就减1,任何时刻计数器为0时对象就被不可能再被使用。
客观地说,引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,也有一些比较著名的应用案例,例如微软COM(Component Object Model)技术、使用ActionScript3的FlashPlayer、Python语言以及在游戏脚本领域中被广泛引用的Squirrel都使用了引用计数算法进行内存管理。但是,Java语言中没有选用引用计数算法来管理内存,其中最主要的原因他很难解决对象之间的相互循环引用的问题。
下面的代码,对象objA和objB都有字段instance,赋值令objA.instance = objB及objB.instance = objA,除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是他们因为互相引用着对方,导致他们的引用计数都不为0,于是引用计数算法无法通知GC收集器回收它们。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package
gc;
/**
* 引用计数算法GC
* @author Madison
* @date 2014-7-12
* testGC()方法执行后,objA和objB会不会被GC呢
*/
public
class
ReferenceCountingGC
{
public
Object instance =
null
;
private
static
final
int
_1MB =
1024
*
1024
;
//这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
private
byte
[] bigSize =
new
byte
[
2
* _1MB];
public
static
void
testGC()
{
ReferenceCountingGC objA =
new
ReferenceCountingGC();
ReferenceCountingGC objB =
new
ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA =
null
;
objB =
null
;
System.gc();
}
}
|
2.根搜索算法(GC Roots Tracing)
通过一系列的“GC Roots"对象作为起始点,从这些节点开始向下搜索,搜索所通过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连接(图论中就是GC Roots到这个对象不可达)时,则证明该对象是不可达的。
虚拟机栈(栈帧中的本地变量表)中的引用的对象
方法区中的类静态属性引用的对象
方法区中的常量引用的对象
本地方法栈中JNI(即一般说的Native方法)的引用的对象
参考文章: JVM内存管理和JVM垃圾回收机制
客观地说,引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,也有一些比较著名的应用案例,例如微软COM(Component Object Model)技术、使用ActionScript3的FlashPlayer、Python语言以及在游戏脚本领域中被广泛引用的Squirrel都使用了引用计数算法进行内存管理。但是,Java语言中没有选用引用计数算法来管理内存,其中最主要的原因他很难解决对象之间的相互循环引用的问题。
下面的代码,对象objA和objB都有字段instance,赋值令objA.instance = objB及objB.instance = objA,除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是他们因为互相引用着对方,导致他们的引用计数都不为0,于是引用计数算法无法通知GC收集器回收它们。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package
gc;
/**
* 引用计数算法GC
* @author Madison
* @date 2014-7-12
* testGC()方法执行后,objA和objB会不会被GC呢
*/
public
class
ReferenceCountingGC
{
public
Object instance =
null
;
private
static
final
int
_1MB =
1024
*
1024
;
//这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
private
byte
[] bigSize =
new
byte
[
2
* _1MB];
public
static
void
testGC()
{
ReferenceCountingGC objA =
new
ReferenceCountingGC();
ReferenceCountingGC objB =
new
ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA =
null
;
objB =
null
;
System.gc();
}
}
|