首页 > 代码库 > 垃圾回收所思所想(一)
垃圾回收所思所想(一)
编程语言更迭至今,几乎没有人没有用过一门不用垃圾回收的语言,一方面因为C之类的语言内存管理较为困难,更重要的是因为语言越高级,越注重实际的业务逻辑,而关于内存管理的代码频繁夹杂在业务中,并不那么自然。
没有垃圾回收的编程体验
#include <stdio.h>
#include <stdlib.h>
struct node {
int value;
struct node2* pnode2;
};
struct node2 {
int value;
};
int main() {
struct node* _node = (struct node*) malloc(sizeof(*_node));
_node->pnode2 = (struct node2*) malloc(sizeof(struct node2));
// Do something ...
free(_node->pnode2);
free(_node);
return 0;
}
在代码如此简单的这个程序里显示出自己申请内存,回收内存的麻烦,试想一个稍有规模的程序里会使用大量的动态内存分配和释放,程序员不得不花费精力在管理内存,防止内存泄露上,以及调试内存问题带来的较大的麻烦。
垃圾回收:自动管理内存
有了垃圾回收之后,将程序员从内存管理的繁杂中解救出来,让程序员能够将更多精力花在业务代码上。
public class Node {
class Node2 {
}
private Node2 node2;
public Node() {
this.node2 = new Node2();
}
public static void main(String[] args) {
Node node = new Node(); // this is node 1.
node = new Node(); // this is node 2.
}
}
用户不需要关心内存到底有没有释放,这些都隐式地被运行时进行处理,除非要释放的引用依然可达,不然垃圾总将被回收。
垃圾回收的条件
因为一个对象A会引用其它的对象B,所以当回收对象A时,它的成员B也将也该递归地"释放",这里"释放"意味着尝试回收,但很有可能引用这个成员B的不止是A,这样的情况下,盲目回收B就会影响到其它的对象,造成一些程序错误,所以,"释放"只是声明A不再引用B,至于B是否回收,就交给垃圾回收器判断。 那么根据上面的过程,有几个必须的元素:
- 对象引用情况的跟踪
能够根据引用情况判断对象是否应该回收,当有任何一个可用的引用时都不应该把对象回收,造成程序潜在的异常。 - 对象类型信息
当确认回收一个对象时,也必须尝试回收它的成员对象,这是根据这个对象所属类型进行成员对象索引的。
垃圾回收算法
垃圾回收遇到的挑战 垃圾回收本质上是对所有对象进行存在性判断,垃圾回收的性能会随着对象的增多而增加; 找出对象是可回收的依据;引发垃圾回收的条件,常常是内存不足,垃圾回收器还要尽量减少内存碎片,能够尽量满足内存的申请。这些问题都是垃圾回收算法需要面对的。
引用计数算法
引用计数是为对象维护一个
)
Object obj = new Object(); // obj: refCount = 1
Object obj2 = obj; // obj: refCount = 2, obj2: refCount = 2
obj = null; // obj: refCount = 1, obj2: refCount = 1
obj2 = null; // obj: refCount = 0, obj2: refCount = 0
在这里不管是obj,还是obj2,它们都仅仅是指向堆上的内存指针而已,而这里的refCount实际上是描述堆上的那块通过new产生的内存的引用次数。
这样的通过计数的方法来释放内存,虽然直接简单,但是存在隐患,就是交叉引用。
public class Sample {
class B {
A a;
}
class A {
B b;
}
public static void main(String[] args) {
B b = new B(); // b: refCount = 1
A a = new A(); // b: refCount = 1, a: refCount = 1
b.a = a; // b: refCount = 1, a: refCount = 2
a.b = b; // b: refCount = 2, a: refCount = 2
a = null; // b: refCount = 2, a: refCount = 1
b = null; // b: refCount = 1, a: refCount = 1
// Memory Leaks...
}
}
两个对象交叉引用,最后如果没有对成员变量先进行手动释放,那么两个对象最后各自的引用次数依然非零,难以回收。
未完待续....