D语言内存管理杂谈

D是一个系统级语言,但是也是一个GC语言(虽然现在GC在可选化,但是有的语言特性是基于GC的,例如:关联数组,数组连接等等)。D是GC语言,但不是全GC语言,D同时也提供了简单的手动管理内存的方式。所以下面我就分为GC,NoGC的方式来说。

GC:

因为D有GC所以,你可以像其他GC语言一样可以直接new 去申请一个对象而不用考虑在哪里释放他,解放内存的管理,不用担心内存泄漏了。对于其他资源的管理,你也可以利用D的RAII特性和scope去管理,很省心。但是D 现在的GC 做不到jvm或者CLR那么给力,对于一些性能和实时行有要求的程序,D现在的GC是力不从心的。

D现在(2016.06)的GC,想对于现在jvm和CLR来说,十分的原始和简陋。D现的GC采用的是标记-清楚算法的保守式GC,GC执行的时候是stw(Stop the world)暂停整个程序的。可怕的是暂停时间不可控:因为每次运行都要扫描整个GC的堆,所以GC的堆越大,GC回收的时间也就越长,而且每次回收的时间是不可确定的。而且因为是保守式的,现在的硬件环境下,在32位系统环境中还是可能存在内存泄漏的。

D有GC是方便了,但是GC有时却又不堪大用,幸好D还提供了手动管理内存的方法,可以自己去控制一些关键的地方。

题外话:
  • 并发GC: 在druntime的github版本库里,有个分支CDGC,这个分支实现posix下的并发GC,最后一次更新是在6年前,不知道为什么没被合并(我E文不大好,也没去扒commit和pull请求)。简单看了下代码,简单说下我理解其并发的实现方式:利用posix的fork去实现的。当需要进行垃圾回收的时候,暂停整个程序,然后fork出一个进程,然后唤醒父进程,在子进程(fork出来的那个进程)里去执行GC的扫描和回收。这样实现了暂停的时间可控(只是fork一个进程的时间),扫描和回收的时候不影响程序执行。具体实现有很多状态判断的,我在简述的时候没有提,只是看后感觉这个的想法和实现很给力的。
  • 精确GC:对于jvm和CLR的那些先进的GC算法,基础都实现精确GC。在DConf2013有演讲提到过一个实现,也pull到主版本库,但是没有合并,原因我也没细看。在今年的GSOC(谷歌编程之夏)启动了精确GC的实现,现在也在实现中,等待结果吧。

虽然,现在D的GC很简陋,但是我想过两年D的GC也会赶上来的。

 

手动管理:

D支持所有C的标准库,所以D中最直接也就是可以使用malloc和free,直接像C的方式使用,对于类和结构体,也有emplace去实现类似C++d的placement new 和destory去调用析够函数去销毁对象。

从D2.069开始引入了新模块:allocator,提供了统一内存分配接口,提供了C的,GC和对齐的内存方式,也提供了make,直接从分配其中分配出指定类型对象。同时还提供了很多内存分配的结构和算法,例如:freelist,Segregator 等等, 方便自己构造内存池和内存重用。

D对于作用与也提供了Unique(所有着) 和RefCounted(引用计数)还有其他一些, 去针对值类型的指针的管理,但是遗憾的是不支持引用类型的引用。

总的来说个人以为现在D的手工管理内存的方式方便程度已经大于和等于C++98方式,但是D没有boost这个神器,相对C++11/14不如。

D的一些特性是基于GC的,而且现在(2016.06)的标准库和运行时很多还是基于GC分配内存的。所以如果你需要整个程序都使用手动管理内存,还是有很多困难的,更多情况下是混合使用。

题外话:

D今年的目标一部分就是在标准库去GC化,下半年社区好像也要去实现引用计数的方式去替代GC的管理,所以D的GC在可选化,虽然GC在进步,也给用户完全不用的自由(虽然个人感觉D这样会更加复杂和庞大)。快的话,明年可能我们就能完全不用D的GC了。

混合管理:

现在写D语言程序,最好的就是混合着使用,利用GC安全的优势,尽量避免GC的劣势。根据我个人经验,在大量的小对象分配的是时候GC的分配器可以快过C的malloc,GC本身就有内存重用,类似一个大大的内存池。个人现在的观点也是把GC当作带保险的内存池。

对于类或者对象数组,所有可能与GC交互的内存都走GC的(虽然也可以走外部分配,然后GC.addRange/removeRange。但是在我的使用经验中,这两个函数的很耗时,过多使用可能比GC几次还耗时),但是如果生命周期可控的,在使用完后,自己手动去释放(destroy,然后GC.free),这样去保证GC堆中一直有未分配内存,去减少GC的触发次数。对于可以保证里面不会引用GC堆中内存的,或者buffer数组的,可以使用外部分配器,去降低GC堆的大小。

对于严格不能暂停的线程,函数中 thread_detachThis 保证GC回收的时候此线程不会暂停,然后需要控制好GC内存引用,使用外部分配(因为GC回收的过程中,申请或者释放GC的内存都会触发内存错误的)。

题外话:

D是GC语言,但是社区还有很多排斥GC的思想,所以D虽然发布很多版本了,GC的本质还是没有变过,不像GO,走GC就是GC,到现在1.5之后GC有质的飞跃。D社区在GC语言基础上却在大力发展手动管理。所以有了现在D内存管理的局面。还好,根据wiki里的计划,今年会两个方向都在优化,手动那边加入引用计数,GC方面在精确化和改进分配。今年年末可以看看社区的结果吧。希望17 能看到D内存管理方面有质的飞跃,不像现在兼得却都不够好,使用中需要权衡。

D的值类型和引用类型,参见下篇博文。

Ps: 个人经验和见解,非使用手册,如果关心用法请去看官方文档。基于D2,D1不再讨论范围,如有错误请您指正。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.