首页 > 代码库 > 垃圾回收所思所想(一)

垃圾回收所思所想(一)

编程语言更迭至今,几乎没有人没有用过一门不用垃圾回收的语言,一方面因为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是否回收,就交给垃圾回收器判断。 那么根据上面的过程,有几个必须的元素:

  1. 对象引用情况的跟踪
    能够根据引用情况判断对象是否应该回收,当有任何一个可用的引用时都不应该把对象回收,造成程序潜在的异常。
  2. 对象类型信息
    当确认回收一个对象时,也必须尝试回收它的成员对象,这是根据这个对象所属类型进行成员对象索引的。

垃圾回收算法

垃圾回收遇到的挑战 垃圾回收本质上是对所有对象进行存在性判断,垃圾回收的性能会随着对象的增多而增加; 找出对象是可回收的依据;引发垃圾回收的条件,常常是内存不足,垃圾回收器还要尽量减少内存碎片,能够尽量满足内存的申请。这些问题都是垃圾回收算法需要面对的。

引用计数算法

引用计数是为对象维护一个

)
,当被引用次数为零时,这个对象应该被回收。

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...
    }
}

两个对象交叉引用,最后如果没有对成员变量先进行手动释放,那么两个对象最后各自的引用次数依然非零,难以回收。

                                      未完待续....