最近编程语言当中,golang无疑是风生水起,年度语言,服务器端语言,并发语言,皇冠可谓不少。golang开发的初衷是替换掉c/c++,作为系统级语言,加上在1.3版本中打算将编译系统从原来c语言开发的plan 9编译器,改为golang实现,可谓野心勃勃。golang最令人赞美的就是简单的语法,你可能花不了一天就能掌握golang的语法,关键字。golang的goroutine和channel给了大家一种简单的并发编程模型(在此指出的是channel是另一种选择,并不是用来替换掉Lock机制的并发编程,而是相互补助)。
在这里,给golang泼泼冷水,或者说,如果golang想成为主流语言,还需要解决哪些重要的问题。
- 一个无锁的sched调度算法。 golang容许你创建成千上万的goroutine,和原生系统级线程不同,goroutine的调度并不是由系统内核来完成,而是golang自己的sched调度系统来完成。golang的sched调度系统采用比较著名的work-steel算法,大家都知道该算法里最核心的一个数据结构就是一个任务队列,如何保证高并发下该队列的正确性是该算法的重点,比较熟悉java的同学应该知道,大师doug lea威廉叔叔的fork-join并发框架采用一个64位 volatile long字段来保证队列的高并发不加锁的实现(注意volatile在java中与c++里面的语义有区别),而golang却采用锁的方式来实现并发访问,这个无疑就掉了一个档次,好在在golang 1.3的计划里面已经明确将sched的无锁列为了重要的工作。
- 一个更轻量级的内存管理框架。大家都知道google有一款很牛的性能分析和加强的框架叫google perftools,该框架里面有一个multi-threaded malloc() 。golang内部的内存管理框架采用的正式该框架,所以如果是golang程序就没有必要再链接perftools来提高性能。该框架对于一个通用框架是非常不错的,然而golang的内存分配在一开始就定义好了内存大小,64位系统为128G内存,所以这种情况下的内存分配再简单不过了,而采用了一个极其复杂的malloc是否有必要?虽社区时有人反应,就连russ cox也表达了需要简化的想法,遗憾的是并没有加入golang 1.3的计划当中。
- 一个优化的垃圾回收算法。 golang目前的垃圾回收机制极其幼稚,当系统使用内存到达上次gc回收的多少比例的时候进行一次全内存的标记扫描回收,默认该比例为2倍,也就是说默认下,64位系统你最多能使用64g内存,当然这是理想情况,现实中,你估计也不会放心在这种gc算法下创建一个大内存系统。CMS在java中得到广泛应用,也是实践中得出最好的gc。golang如果想成为主流语言,必须在gc上和java一个级别,否则只能常常简单golang vs Python的文章。目前来看,提高垃圾回收精度(golang在不确定内存中保存的对象时,会默认4个字节一次的依次扫描内存,这个实在是太慢啦),扫描阶段不暂停系统比较切合实际,分代分区就要长远来看吧。
- 一个Heap Dump工具。如果你不知道golang程序实例内存是如何分布的,想要优化你的程序,就是盲人摸象。网上有一位美团的同学,在一个线上系统中,发现使用map会使gc变慢,他只能猜测是gc对map有特殊的处理导致,其实实际的原因是一个map在gc的时候会保留一把锁,而gc无法并发的回收该map,如果该map是一个主体结构,cms gc就变成了 单线程扫描gc了,好在该问题在golang 1.2已经修复。可喜的是golang 1.3就有可能发布heap dump工具。 采用copy stack替换现有的split stack。golang最值得夸耀的是轻量级的goroutine,goroutine的管理都有golang自己来实现,每一个goroutine都会默认在heap当中分配一个4k的内存块来作为该goroutine的stack。和java不同的是,golang的stack理论上可以无限大,这要得益于golang的split stack机制,当goroutine stack满时,golang会再分配一个块内存来补充,从而形成一个无限大的stack。然而成也萧何,败也萧何。当stack变小时,golang就会释放掉空闲的stack,如果一个长时间运行的程序,stack肯定会频繁的伸缩,这样照成的内存分配和释放相当频繁,从而影响性能。好在目前ross cox已经决定采用copy stack的方式来解决该问题,而copy stack会不会造成内存多余占用的问题呢,比如一个goroutine一次大stack,以后的stack都很小,让我们拭目以待吧。
- 采用copy stack替换现有的split stack。 golang最值得夸耀的是轻量级的goroutine,goroutine的管理都有golang自己来实现,每一个goroutine都会默认在heap当中分配一个4k的内存块来作为该goroutine的stack。和java不同的是,golang的stack理论上可以无限大,这要得益于golang的split stack机制,当goroutine stack满时,golang会再分配一个块内存来补充,从而形成一个无限大的stack。然而成也萧何,败也萧何。当stack变小时,golang就会释放掉空闲的stack,如果一个长时间运行的程序,stack肯定会频繁的伸缩,这样照成的内存分配和释放相当频繁,从而影响性能。好在目前ross cox已经决定采用copy stack的方式来解决该问题,而copy stack会不会造成内存多余占用的问题呢,比如一个goroutine一次大stack,以后的stack都很小,让我们拭目以待吧。
golang在语法上我想是无可挑剔了,上面是我认为golang急需解决的问题,如果能解决这些问题,特别是gc和调度,我想我会头也不回的投入golang的阵营当中。
有疑问加站长微信联系(非本文作者)