zoukankan      html  css  js  c++  java
  • 垃圾回收所思所想(一)

    编程语言更迭至今,几乎没有人没有用过一门不用垃圾回收的语言,一方面因为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. 对象类型信息
      当确认回收一个对象时,也必须尝试回收它的成员对象,这是根据这个对象所属类型进行成员对象索引的。

    垃圾回收算法

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

    引用计数算法

    引用计数是为对象维护一个被引用次数(Reference count),当被引用次数为零时,这个对象应该被回收。

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

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

                                          未完待续....
  • 相关阅读:
    【Codevs 2630】宝库通道
    【Codevs 2115】数集分割
    【HDU2037】今年暑假不AC
    【Codeforces】Round #376 (Div. 2)
    【Dairy】2016.10.17-1 OIer最悲剧的事情
    【Codevs 3115】高精度练习之减法
    【Codevs1080】质数环
    【T^T 1871】获取敌情
    【Codevs3151】交通管制I
    【Codeforces】716D Complete The Graph
  • 原文地址:https://www.cnblogs.com/pier2/p/gc.html
Copyright © 2011-2022 走看看