D语言中的垃圾收集机制

原创 2005年03月02日 22:11:00

垃圾收集

D 是一种全面采用垃圾收集的语言。这意味着它从来不用释放内存。只需要按需分配,然后由垃圾收集程序周期性的将所有未使用的内存返回给可用内存池。

C 和 C++ 程序员习惯于显式的管理内存分配和释放,很可能会怀疑垃圾收集的好处和功效。对一开始就采用垃圾收集设计的新项目和用垃圾收集改良的现有项目的经验表明:

  • 采用垃圾收集的程序更快。这有些违反直觉,但是其原理有:
    • 引用计数是解决显式内存分配问题的常用解决方案。实现赋值时递增和递减操作的代码通常是程序缓慢的原因之一。将其隐藏在智能指针类之后并不能提高速度。 (无论如何,引用计数也不是全面的解决方案,因为循环引用从不会被删除。)
    • 析构函数用来释放对象获得的资源。对于大多数类来说,这个资源就是内存。采用垃圾收集,大多数析构函数都可以完全抛弃。
    • 当对象在堆栈上分配时,那些负责释放内存的析构函数就变得十分重要。对于这些函数来说,必须确立一种机制以使异常发生时,每一个函数帧中的析构函数都会被调用以释放对象持有的内存。如果析构函数变得不相关,就没有设置特殊的堆栈帧用于处理异常,这样运行也会更快。
    • 内存管理代码总量会变少。程序越大,它在cache中的部分的越少;它的调页越多,它运行的越慢。
    • 垃圾收集只会在内存变得紧张时才会运行。当内存尚且宽裕时,程序将全速运行,不会在释放内存上花费任何时间。
    • 相对于过去的缓慢的垃圾收集程序,现代的垃圾收集程序要先进得多。分代,复制收集程序在很大程度上克服了早期的标记&清除算法的低效。
    • 现代垃圾收集程序进行堆紧缩。堆紧缩将减少程序引用的页的数量,这意味着内存访问命中率将更高,交换将更少。
    • 采用垃圾收集的程序不会因为内存泄漏的累积而崩溃。
  • 垃圾收集程序回收不被使用的内存,因此不会有“内存泄露”问题。内存泄露会使长期运行的应用程序逐渐耗尽内存直至使系统崩溃。采用 GC 的程序拥有更长期的稳定性。
  • 采用垃圾收集的程序 有更少的难以发现的指针bug。这是因为没有指向已经释放的内存的悬挂指针。因为没有显式的内存管理代码,也就不可能有相应的bug。
  • 采用垃圾收集的程序的开发和调试更快,因为不用开发、调试、测试或维护显式的释放代码。
  • 采用垃圾收集的程序会明显更小,因为没用用于管理内存释放的代码,也不需要处理内存释放异常的代码。
垃圾收集并不是万金油。它有以下缺点:
  • 内存收集何时运行是不可预测的,所以程序可能在任何位置暂停。
  • 运行内存收集的时间是没有上界的。尽管在实践中它的运行通常很快,但无法保证这一点。
  • 除了收集程序以外的所有线程在回收进行时都会停止运行。
  • 垃圾收集程序也许会留下一些本该回收的内存。在实践中,这不是什么大问题,因为显式内存回收程序通常会泄露一些内存,这致使它们最终耗尽所有内存,另一个理由就是显式内存回收程序通常会把内存放回自己的内部内存池中而不是把内存交还给操作系统。
  • 垃圾收集应该被实现为一个基本的操作系统内核服务。但是因为现实并非如此,就造成了采用垃圾收集的程序被迫带着它们的垃圾收集实现到处跑。尽管这个实现可以被做成一个共享 DLL ,它也还是程序的一部分。
这些限制可以通过采用 内存管理 中介绍的技术来缓解。

垃圾收集如何工作

尚未完成。

外部代码同垃圾收集对象的协作

垃圾收集程序在静态数据段和每个线程的堆栈和寄存器中寻找根。如果对象唯一的根不在它们中,收集程序将会释放它占有的内存。

如果要避免这种行为,需要

  • 为对象维护一个根,该根要位于收集程序扫描的区域之内。
  • 使用外部代码的存储分配程序重新分配一个对象或者使用 C 运行时库的 malloc/free 。

指针和垃圾收集程序

D 中的指针可以被粗略地分为两类:指向垃圾收集内存的指针和其他的指针。后者如由 C 的 malloc() 得到的指针,从 C 库中得到的指针,指向静态数据的指针,指向堆栈上对象的指针,等等。对于这些指针,所有 C 中的合法指针的操作都可以应用于其上。

但是,对于垃圾收集指针和引用,对指针的操作就有一些限制了。这些限制是不会造成太大的缺陷,但却会使垃圾收集程序的设计灵活许多。

未定义行为:

  • 将指针同其他的值进行异或,如同在 C 中在链表中用异或保存指针那种小技巧。不要使用异或技巧交换两个指针的值。
  • 使用类型转换和其他的小技巧将指针存储在非指针变量内。
    	void* p;
    	...
    	int x = cast(int)p;	// 错误:行为未定义
    	
    垃圾收集程序构建根集时不会扫描非指针类型。

  • 利用特定的指针对齐的特点在低位或者高位存储 flag :
    	p = cast(void*)(cast(int)p | 1);	// 错误:行为未定义
    	
  • 将可能指向垃圾收集堆的值转换为指针:
    	p = cast(void*)12345678;	// 错误:行为未定义
    	
  • 不要将不同于 null 的“魔数”存入指针。

  • 将指针值写入到磁盘然后再从磁盘读入到内存。

  • 使用指针值计算散列值。采用复制技术的垃圾收集程序可能会任意地在内存中移动对象,这会使散列值失效。

  • 依赖指针的顺序:
    	if (p1 < p2)		// 错误:行为未定义
    	    ...
    	
    因此,再一次提醒,垃圾收集程序可能会在内存中移动对象。

  • 给指针加上或者减去一个偏移量是结果指向垃圾收集程序原来为对象分配的范围之外。
    	char* p = new char[10];
    	char* q = p + 6;		// ok
    	q = p + 11;		// 错误:行为未定义
    	q = p - 1;		// 错误:行为未定义
    	
可以依赖的行为:
  • 使用联合共享指针的存储空间:
    	union U { void* ptr; int value }
    	
    但是,使用这样的联合来替代 cast(int) 的结果是未定义的。

  • 如果存在指向垃圾收集对象对象内部的指针,就不必维护指向对象开始处的指针。
    	char[] p = new char[10];
    	char[] q = p[3..6];
    	// q 完全可以用来指向整个对象,不需要同时保留 p 。
    	
程序员可以不用指针完成大部分任务。D 提供的各种特征使得人们在决大多数情况下都不需要显式地使用指针,这些特征包括:饮用对象、动态数组和垃圾收集。提供指针的目的是成功地同 C API 衔接并完成一些低级的工作。

与垃圾收集程序协作

垃圾收集程序并不能解决所有的内存释放问题。例如,如果保留了一个指向一大块数据的指针,那么就算它再也用不到了,垃圾收集程序也无法回收这块空间。为了解决这个问题,一个不错的实践是在不再使用一个对象时将指向它的引用或指针设为 null 。

这个建议只适用于静态引用或者嵌入到其他对象内的引用。它对于那些存储在堆栈上的引用没什么意义,因为收集程序并不扫描栈顶之上的部分,并且新的堆栈帧总会被初始化。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Java 语言的垃圾收集机制

Garbage Collection 目的: 去清除不在使用的对象所占用的内存空间 采用跟踪收集器 去递归的遍历一组对象,确定可到达的对象,进行标记。 删除的时候,进行堆栈扫描,删除未标记的对象。 、...

Java的垃圾收集机制

【深入Java虚拟机】之八:Java垃圾收集机制

文章转载至:http://blog.csdn.net/ns_code/article/details/18076173

【深入Java虚拟机】之八:Java垃圾收集机制

转载请注明出处:http://blog.csdn.net/ns_code/article/details/18076173 对象引用     Java中的垃圾回...

Java垃圾收集机制

本文是《深入理解Java虚拟机》一书中第三章的读书总结。 前面介绍了Java内存结构和HotSpot虚拟机在堆内存中管理对象的过程。不过,在Java程序中对象的创建是非常频繁的,而内存的大小又是有限的...

Java虚拟机(五)——垃圾收集机制

垃圾回收介绍   Java虚拟机内存划分讲到了Java 内存运行时区域的各个部分,其中程序计数器,虚拟机栈,本地方法栈三个区域随线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出有条不紊地执行着出栈...

Dalvik虚拟机垃圾收集机制简要介绍和学习计划

伴随着“Dalvik is dead,long live Dalvik“这行AOSP代码提交日志,在Android5.0中,ART运行时取代了Dalvik虚拟机。虽然Dalvik虚拟机不再使用,但是它...

PHP的GC垃圾收集机制

PHP 有一个非常简单的垃圾收集器,它实际上将对不再位于内存范围(scope)中的对象进行垃圾收集。垃圾收集的内部方式是使用一个引用计数器,因此当计数器达到 0 时(意味着对该对象的引用都不可用),对...

垃圾收集机制——标识无用变量策略

先验知识:JavaScript具有自动垃圾收集机制,就是说执行环境会负责管理代码执行过程中使用的内存。在C和C++之类语言中都是手工追踪内存,这会造成许多问题。而JavaScript不需要关心内存使用...

【深入Java虚拟机】之八:Java垃圾收集机制

对象引用     Java中的垃圾回收一般是在Java堆中进行,因为堆中几乎存放了Java中所有的对象实例。谈到Java堆中的垃圾回收,自然要谈到引用。在JDK1.2之前,Java中的引...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)