成为主流语言 Go 语言急需解决的几个问题

最近编程语言当中,Go 语言无疑是风生水起,年度语言,服务器端语言,并发语言,皇冠可谓不少。Go 语言开发的初衷是替换掉c/c++ ,作为系统级语言,加上在1.3版本中打算将编译系统从原来c语言开发的plan 9编译器,改为Go 语言实现,可谓野心勃勃。Go 语言最令人赞美的就是简单的语法,你可能花不了一天就能掌握Go 语言的语法,关键字。Go 语言的goroutine和channel给了大家一种简单的并发编程模型(在此指出的是channel是另一种选择,并不是用来替换掉Lock机制的并发编程,而是相互补助)。

在这里,给Go 语言泼泼冷水,或者说,如果Go 语言想成为主流语言,还需要解决哪些重要的问题。

一个无锁的sched调度算法。 Go 语言容许你创建成千上万的goroutine,和原生系统级线程不同,goroutine的调度并不是由系统内核来完成,而是Go 语言自己的sched调度系统来完成。Go 语言的sched调度系统采用比较著名的work-steel算法,大家都知道该算法里最核心的一个数据结构就是一个任务队列,如何保证高并发下该队列的正确性是该算法的重点,比较熟悉java的同学应该知道,大师doug lea威廉叔叔的fork-join并发框架采用一个64位 volatile long字段来保证队列的高并发不加锁的实现(注意volatile在java中与c++里面的语义有区别),而Go 语言却采用锁的方式来实现并发访问,这个无疑就掉了一个档次,好在在Go 语言 1.3的计划里面已经明确将sched的无锁列为了重要的工作。

一个更轻量级的内存管理框架。大家都知道google有一款很牛的性能分析和加强的框架叫google perftools,该框架里面有一个multi-threaded malloc() 。Go 语言内部的内存管理框架采用的正式该框架,所以如果是Go 语言程序就没有必要再链接perftools来提高性能。该框架对于一个通用框架是非常不错的,然而Go 语言的内存分配在一开始就定义好了内存大小,64位系统为128G内存,所以这种情况下的内存分配再简单不过了,而采用了一个极其复杂的malloc是否有必要?虽社区时有人反应,就连russ cox也表达了需要简化的想法,遗憾的是并没有加入Go 语言 1.3的计划当中。

一个优化的垃圾回收算法。 Go 语言目前的垃圾回收机制极其幼稚,当系统使用内存到达上次gc回收的多少比例的时候进行一次全内存的标记扫描回收,默认该比例为2倍,也就是说默认下,64位系统你最多能使用64g内存,当然这是理想情况,现实中,你估计也不会放心在这种gc算法下创建一个大内存系统。CMS在java中得到广泛应用,也是实践中得出最好的gc。Go 语言如果想成为主流语言,必须在gc上和java一个级别,否则只能常常简单Go 语言 vs Python的文章。目前来看,提高垃圾回收精度(Go 语言在不确定内存中保存的对象时,会默认4个字节一次的依次扫描内存,这个实在是太慢啦),扫描阶段不暂停系统比较切合实际,分代分区就要长远来看吧。

一个Heap Dump工具。如果你不知道Go 语言程序实例内存是如何分布的,想要优化你的程序,就是盲人摸象。网上有一位美团的同学,在一个线上系统中,发现使用map会使gc变慢,他只能猜测是gc对map有特殊的处理导致,其实实际的原因是一个map在gc的时候会保留一把锁,而gc无法并发的回收该map,如果该map是一个主体结构,cms gc就变成了 单线程扫描gc了,好在该问题在Go 语言 1.2已经修复。可喜的是Go 语言 1.3就有可能发布heap dump工具。 采用copy stack替换现有的split stack。Go 语言最值得夸耀的是轻量级的goroutine,goroutine的管理都有Go 语言自己来实现,每一个goroutine都会默认在heap当中分配一个4k的内存块来作为该goroutine的stack。和java不同的是,Go 语言的stack理论上可以无限大,这要得益于Go 语言的split stack机制,当goroutine stack满时,Go 语言会再分配一个块内存来补充,从而形成一个无限大的stack。然而成也萧何,败也萧何。当stack变小时,Go 语言就会释放掉空闲的stack,如果一个长时间运行的程序,stack肯定会频繁的伸缩,这样照成的内存分配和释放相当频繁,从而影响性能。好在目前ross cox已经决定采用copy stack的方式来解决该问题,而copy stack会不会造成内存多余占用的问题呢,比如一个goroutine一次大stack,以后的stack都很小,让我们拭目以待吧。

采用copy stack替换现有的split stack。 Go 语言最值得夸耀的是轻量级的goroutine,goroutine的管理都有Go 语言自己来实现,每一个goroutine都会默认在heap当中分配一个4k的内存块来作为该goroutine的stack。和java不同的是,Go 语言的stack理论上可以无限大,这要得益于Go 语言的split stack机制,当goroutine stack满时,Go 语言会再分配一个块内存来补充,从而形成一个无限大的stack。然而成也萧何,败也萧何。当stack变小时,Go 语言就会释放掉空闲的stack,如果一个长时间运行的程序,stack肯定会频繁的伸缩,这样照成的内存分配和释放相当频繁,从而影响性能。好在目前ross cox已经决定采用copy stack的方式来解决该问题,而copy stack会不会造成内存多余占用的问题呢,比如一个goroutine一次大stack,以后的stack都很小,让我们拭目以待吧。

Go 语言在语法上我想是无可挑剔了,上面是我认为Go 语言急需解决的问题,如果能解决这些问题,特别是gc和调度,我想我会头也不回的投入Go 语言的阵营当中。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。