关于并发内存占用

729 views
Skip to first unread message

Yan Unon

unread,
Jan 8, 2013, 10:43:48 AM1/8/13
to golang...@googlegroups.com
最近在使用[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同时,我也使用了nodejs版本[shadowsocks-nodejs](https://github.com/clowwindy/shadowsocks-nodejs)测试,并发上百个连接内存占用只有40%左右。

后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)

请教怎么做能够让内存占用降下来?

Yan Unon

unread,
Jan 8, 2013, 10:49:06 AM1/8/13
to golang...@googlegroups.com

不好意思,忘了格式化内容。重发一下。

最近在使用shadowsocks-go作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同时,我也使用了nodejs版本shadowsocks-nodejs测试,并发上百个连接内存占用只有40%左右。

后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)

请教怎么做能够让内存占用降下来?



2013/1/8 Yan Unon <yan...@gmail.com>

minux

unread,
Jan 8, 2013, 11:30:26 AM1/8/13
to golang...@googlegroups.com

2013/1/8 Yan Unon <yan...@gmail.com>

最近在使用[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同时,我也使用了nodejs版本[shadowsocks-nodejs](https://github.com/clowwindy/shadowsocks-nodejs)测试,并发上百个连接内存占用只有40%左右。
你的OpenVZ系统支持Vswap么?是64位系统么? 

后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)
没看懂这段,75个goroutine能到128MiB的话,那可是平均下来每个goroutine 1MiB+呀

Monnand

unread,
Jan 8, 2013, 3:22:04 PM1/8/13
to golang...@googlegroups.com
On 01/08/2013 10:49 AM, Yan Unon wrote:
> 不好意思,忘了格式化内容。重发一下。

邮件列表里大家一般都会用纯文本。

>
> 最近在使用shadowsocks-go <https://github.com/shadowsocks/shadowsocks-go>
> 作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果
> 十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同
> 时,我也使用了nodejs版本shadowsocks-nodejs
> <https://github.com/clowwindy/shadowsocks-nodejs>测试,并发上百个连接内
> 存占用只有40%左右。
>

我猜一下,你的VPS上跑的是32位系统吧?

> 后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,然后
> 开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是128MB内存
> 连75个简单goroutine都撑不起。(不知道这样分析对不对)
>

这是很常见的做法。

> 请教怎么做能够让内存占用降下来?
>
>
>
> 2013/1/8 Yan Unon <yan...@gmail.com <mailto:yan...@gmail.com>>
>
> 最近在使用[shadowsocks-go](https://github.com/shadowsocks
> /shadowsocks-go)作为翻墙工具,我把服务器端部署在一个内存为128MB的
> OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后
> 因为不能开辟内存而崩掉。同时,我也使用了nodejs版本[shadowsocks-
> nodejs](https://github.com/clowwindy/shadowsocks-nodejs)测试,并发上
> 百个连接内存占用只有40%左右。
>
> 后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,
> 然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是
> 128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)
>
> 请教怎么做能够让内存占用降下来?
>
>
> --
> --
> 官网: http://golang-china.org/
> IRC: irc.freenode.net #golang-china
> @golangchina
>
>
>

Yufei Chen

unread,
Jan 8, 2013, 9:55:34 PM1/8/13
to golang...@googlegroups.com


On Tuesday, January 8, 2013 11:49:06 PM UTC+8, yanunon wrote:

不好意思,忘了格式化内容。重发一下。

最近在使用shadowsocks-go作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同时,我也使用了nodejs版本shadowsocks-nodejs测试,并发上百个连接内存占用只有40%左右。

我在维护 shadowsocks-go,在 OpenVZ 上目前是有这个问题,很抱歉没有在项目主页上说明。
麻烦你帮我创建一个 issue report 吧?

Yufei Chen

unread,
Jan 8, 2013, 10:16:37 PM1/8/13
to golang...@googlegroups.com

On Wednesday, January 9, 2013 12:30:26 AM UTC+8, minux wrote:

2013/1/8 Yan Unon <yan...@gmail.com>
最近在使用[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同时,我也使用了nodejs版本[shadowsocks-nodejs](https://github.com/clowwindy/shadowsocks-nodejs)测试,并发上百个连接内存占用只有40%左右。
你的OpenVZ系统支持Vswap么?是64位系统么? 
我的 OpenVZ 上是 64bit Debian。
对 OpenVZ 不太了解,Google 了下 vswap 的介绍,还是想不到它对 Go 会有什么影响。 

后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)
 
对每个新的 client 连接,创建一个 goroutine 负责解析 request,建立到 server 的连接 (通常是 web server)。
之后由 2 goroutine 负责传递数据,分别是 client->server 和 server->client,每个使用 4K 的 buffer。(这里其实可以省掉一个 goroutine,不过实现方便期间用了两个。)

对这种处理方式我有点担心 Go 会创建很多线程,不知道有没有其他更好的处理方式。

没看懂这段,75个goroutine能到128MiB的话,那可是平均下来每个goroutine 1MiB+呀
 
我在 64bit native Debian 6 上做过一点内存占用的测试。
单独监听一个端口内存占用在 3M 多,监听 1000 个端口内存占用在 10M 不到。
单个 goroutine 的开销正如文档所说只有几 k。

Yufei Chen

unread,
Jan 8, 2013, 10:19:19 PM1/8/13
to golang...@googlegroups.com
On Tuesday, January 8, 2013 11:49:06 PM UTC+8, yanunon wrote:

不好意思,忘了格式化内容。重发一下。

最近在使用shadowsocks-go作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同时,我也使用了nodejs版本shadowsocks-nodejs测试,并发上百个连接内存占用只有40%左右。

后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)

请教怎么做能够让内存占用降下来?

目前 shadowsocks-go 相比其他实现的功能多一些,比如支持多用户,可以运行时更改密码。
如果只是自己使用的话可以考虑使用 shadowsocks-libuv。 

Yan Unon

unread,
Jan 8, 2013, 11:56:46 PM1/8/13
to golang...@googlegroups.com



2013/1/9 Yufei Chen <cyfd...@gmail.com>



On Tuesday, January 8, 2013 11:49:06 PM UTC+8, yanunon wrote:

不好意思,忘了格式化内容。重发一下。

最近在使用shadowsocks-go作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同时,我也使用了nodejs版本shadowsocks-nodejs测试,并发上百个连接内存占用只有40%左右。

我在维护 shadowsocks-go,在 OpenVZ 上目前是有这个问题,很抱歉没有在项目主页上说明。
麻烦你帮我创建一个 issue report 吧?

已经添加。我也在本地机器测试了,内存占用比较正常,是否有可能是Go与OpenVZ之间的问题?
 

后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)请教怎么做能够让内存占用降下来?

--

Wendal Chen

unread,
Jan 9, 2013, 12:13:46 AM1/9/13
to golang...@googlegroups.com
翻墙用ssh -D不就搞定了,何须这种东西
--
Wendal Chen
GuangDong China

Monnand

unread,
Jan 9, 2013, 2:06:20 AM1/9/13
to golang...@googlegroups.com
On 01/08/2013 09:55 PM, Yufei Chen wrote:
>
>
> On Tuesday, January 8, 2013 11:49:06 PM UTC+8, yanunon wrote:
>
> 不好意思,忘了格式化内容。重发一下。
>
> 最近在使用shadowsocks-go <https://github.com/shadowsocks
> /shadowsocks-go>作为翻墙工具,我把服务器端部署在一个内存为128MB的
> OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后
> 因为不能开辟内存而崩掉。同时,我也使用了nodejs版本shadowsocks-nodejs
> <https://github.com/clowwindy/shadowsocks-nodejs>测试,并发上百个连
> 接内存占用只有40%左右。
>
> 我在维护 shadowsocks-go,在 OpenVZ 上目前是有这个问题,很抱歉没有在项目
> 主页上说明。
> 麻烦你帮我创建一个 issue report 吧?

不好意思,我要OT一下。

Yufei,

请教一下,这个shadowsocks和ssh,VPN等翻墙方法有什么不同?

我大概看了一下代码,只看了encrypt.go这个文件[0],这是一个古典加密算法的
变形吧?没搞错的话,直接用词频分析就可以破解的[1]。还是我有内容没有看到吗?

[0]:
https://github.com/shadowsocks/shadowsocks-go/blob/master/shadowsocks/encrypt.go
[1]: http://en.wikipedia.org/wiki/Frequency_analysis

>
> 后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,
> 然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是
> 128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)请教怎
> 么做能够让内存占用降下来?
>

Chen Yufei

unread,
Jan 9, 2013, 2:35:26 AM1/9/13
to golang...@googlegroups.com

On 2013年1月9日Wednesday at 下午3:06, Monnand wrote:
> On 01/08/2013 09:55 PM, Yufei Chen wrote:
> > 我在维护 shadowsocks-go,在 OpenVZ 上目前是有这个问题,很抱歉没有在项目
> > 主页上说明。
> > 麻烦你帮我创建一个 issue report 吧?
>
>
> 不好意思,我要OT一下。
>
> Yufei,
>
> 请教一下,这个shadowsocks和ssh,VPN等翻墙方法有什么不同?
这个项目是 clowwindy (github, twitter id) 发起的,他设计 shadowsocks 最初的目的是避免 ssh 需要保持长连接,而且需要用户认证过程。
这样的设计在笔记本休眠后唤醒、网络条件差导致经常断线的情况下可以避免花很多时间去重连。
>
> 我大概看了一下代码,只看了encrypt.go这个文件[0],这是一个古典加密算法的
> 变形吧?没搞错的话,直接用词频分析就可以破解的[1]。还是我有内容没有看到吗?

的确是非常弱的加密。不过目的只是骗过墙,目前也达到目的了。
其他加密算法在考虑中,node-js 和 libuv 的实现中已经加入了 rc4 加密。
>
> [0]:
> https://github.com/shadowsocks/shadowsocks-go/blob/master/shadowsocks/encrypt.go
> [1]: http://en.wikipedia.org/wiki/Frequency_analysis
>
> >
> > 后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,
> > 然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是
> > 128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)请教怎
> > 么做能够让内存占用降下来?
> >
> > --
> > --
> > 官网: http://golang-china.org/
> > IRC: irc.freenode.net (http://irc.freenode.net) #golang-china
> > @golangchina
>
>
>
> --
> --
> 官网: http://golang-china.org/
> IRC: irc.freenode.net (http://irc.freenode.net) #golang-china
> @golangchina



Grissiom

unread,
Jan 9, 2013, 2:40:02 AM1/9/13
to golang...@googlegroups.com
2013/1/9 Chen Yufei <cyfd...@gmail.com>


On 2013年1月9日Wednesday at 下午3:06, Monnand wrote:
> On 01/08/2013 09:55 PM, Yufei Chen wrote:
> > 我在维护 shadowsocks-go,在 OpenVZ 上目前是有这个问题,很抱歉没有在项目
> > 主页上说明。
> > 麻烦你帮我创建一个 issue report 吧?
>
>
> 不好意思,我要OT一下。
>
> Yufei,
>
> 请教一下,这个shadowsocks和ssh,VPN等翻墙方法有什么不同?
这个项目是 clowwindy (github, twitter id) 发起的,他设计 shadowsocks 最初的目的是避免 ssh 需要保持长连接,而且需要用户认证过程。
这样的设计在笔记本休眠后唤醒、网络条件差导致经常断线的情况下可以避免花很多时间去重连。
>
> 我大概看了一下代码,只看了encrypt.go这个文件[0],这是一个古典加密算法的
> 变形吧?没搞错的话,直接用词频分析就可以破解的[1]。还是我有内容没有看到吗?

的确是非常弱的加密。不过目的只是骗过墙,目前也达到目的了。
其他加密算法在考虑中,node-js 和 libuv 的实现中已经加入了 rc4 加密。

单纯加密也不一定靠谱,人家万一用流量分析呢?……

--
Cheers,
Grissiom

Chen Yufei

unread,
Jan 9, 2013, 9:11:25 AM1/9/13
to golang...@googlegroups.com
OpenVZ 上运行 Go 程序占用大量内存的问题在 golang-nuts 上已经讨论过了。
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/4Y-SO5QnGyY

OpenVZ 里统计 memory 占用时统计的是 virtual memory 的使用,但 Go runtime 初始化的时候会预留一大块 VM address space。所以启动一个 Go 程序后,用 free 马上会看到少了 80M 的内存,但其实 RES 只有 3M。

minux 提到可以修改 go 源代码里预留 virtual memory 的代码来限制预留 VM 的大小
https://groups.google.com/d/msg/golang-nuts/4Y-SO5QnGyY/pKuOjYVh3n4J

如果 VPS 支持 vSwap,virtual memory 占用不会引起问题
https://groups.google.com/d/msg/golang-nuts/4Y-SO5QnGyY/rc1OEx7Zi3cJ



另外用 ulimit -Sv 限制 VM 占用的方法会影响 Go 程序的正常执行,所以这个办法没法使用。


On 2013年1月9日Wednesday at 下午3:06, Monnand wrote:

minux

unread,
Jan 9, 2013, 9:22:43 AM1/9/13
to golang...@googlegroups.com

2013/1/9 Chen Yufei <cyfd...@gmail.com>

OpenVZ 上运行 Go 程序占用大量内存的问题在 golang-nuts 上已经讨论过了。
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/4Y-SO5QnGyY

OpenVZ 里统计 memory 占用时统计的是 virtual memory 的使用,但 Go runtime 初始化的时候会预留一大块 VM address space。所以启动一个 Go 程序后,用 free 马上会看到少了 80M 的内存,但其实 RES 只有 3M。
倒不是因为预留了很大的VM空间导致在OpenVZ上用不了,而是因为预留的那么大的VM
地址空间需要很大的一片bitmap来保存metadata,在正常的系统上,这是.bss段的内容,
由于是零初始化的区域所以程序不访问的地方实际上并不占用物理内存,因而是不计入
内存使用量的。
举例来说,以linux/amd64 Go tip为例,size(1) bin/go的输出是:
   text      data     bss                       dec                 hex            filename
5860433  112512 268572232       274545177       105d3a19        bin/go
看到第二行第三个数了么?bss有256MiB之多!OpenVZ如果不支持Vswap的话,会直接把
这部分内存计入你使用的内存里,于是一下子就觉得Go在OpenVZ上用内存飞快了。
这应该算是OpenVZ的bug。后来支持Vswap的就没这个问题了。所以我上来就问你的
OpenVZ提供商是否支持Vswap。

minux 提到可以修改 go 源代码里预留 virtual memory 的代码来限制预留 VM 的大小
https://groups.google.com/d/msg/golang-nuts/4Y-SO5QnGyY/pKuOjYVh3n4J
恩 这个解决方案是对的。 只不过那个邮件列表主题里面开始一部分对于原因的分析不对。

Chen Yufei

unread,
Jan 9, 2013, 9:53:12 AM1/9/13
to golang...@googlegroups.com
On 2013年1月9日Wednesday at 下午10:22, minux wrote:
>
> 2013/1/9 Chen Yufei <cyfd...@gmail.com (mailto:cyfd...@gmail.com)>
> > OpenVZ 上运行 Go 程序占用大量内存的问题在 golang-nuts 上已经讨论过了。
> > https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/4Y-SO5QnGyY
> >
> > OpenVZ 里统计 memory 占用时统计的是 virtual memory 的使用,但 Go runtime 初始化的时候会预留一大块 VM address space。所以启动一个 Go 程序后,用 free 马上会看到少了 80M 的内存,但其实 RES 只有 3M。
> 倒不是因为预留了很大的VM空间导致在OpenVZ上用不了,而是因为预留的那么大的VM
> 地址空间需要很大的一片bitmap来保存metadata,在正常的系统上,这是.bss段的内容,
> 由于是零初始化的区域所以程序不访问的地方实际上并不占用物理内存,因而是不计入
> 内存使用量的。
> 举例来说,以linux/amd64 Go tip为例,size(1) bin/go的输出是:
> text data bss dec hex filename
> 5860433 112512 268572232 274545177 105d3a19 bin/go
>
> 看到第二行第三个数了么?bss有256MiB之多!OpenVZ如果不支持Vswap的话,会直接把
> 这部分内存计入你使用的内存里,于是一下子就觉得Go在OpenVZ上用内存飞快了。
> 这应该算是OpenVZ的bug。后来支持Vswap的就没这个问题了。所以我上来就问你的
> OpenVZ提供商是否支持Vswap。

谢谢,明白原因了 :)
我再去看看 vswap 究竟是什么。

这么看来用 ulimit 限制 VM 占用是没用了。不过用这个方法的时候发现个现象:

- 限制 VM 最多占用 90M,程序启动后用了 86M,dns 解析时报找不到 host
- 限制 VM 最多 100M 能做 DNS 解析,不过程序执行一段时间后会由于无法创建新的线程而 abort 掉

创建新的线程应该是由于要做 dns 解析吧,不过限制 86M 时连一次 dns 解析都无法完成有些奇怪,毕竟刚启动时就已经有 6 个线程。

这个 use case 有点奇怪,不过比较好奇这个现象的原因。
> >
> > minux 提到可以修改 go 源代码里预留 virtual memory 的代码来限制预留 VM 的大小
> > https://groups.google.com/d/msg/golang-nuts/4Y-SO5QnGyY/pKuOjYVh3n4J
>
> 恩 这个解决方案是对的。 只不过那个邮件列表主题里面开始一部分对于原因的分析不对。
> >
> > 如果 VPS 支持 vSwap,virtual memory 占用不会引起问题
> > https://groups.google.com/d/msg/golang-nuts/4Y-SO5QnGyY/rc1OEx7Zi3cJ
>
> --
> --
> 官网: http://golang-china.org/
> IRC: irc.freenode.net (http://irc.freenode.net) #golang-china
> @golangchina
>
>
>



steve wang

unread,
Jan 9, 2013, 10:15:59 AM1/9/13
to golang...@googlegroups.com
那你的进程崩溃的原因是什么呢?

minux

unread,
Jan 9, 2013, 4:21:29 PM1/9/13
to golang...@googlegroups.com

2013/1/9 Chen Yufei <cyfd...@gmail.com>
这么看来用 ulimit 限制 VM 占用是没用了。不过用这个方法的时候发现个现象:

- 限制 VM 最多占用 90M,程序启动后用了 86M,dns 解析时报找不到 host
- 限制 VM 最多 100M 能做 DNS 解析,不过程序执行一段时间后会由于无法创建新的线程而 abort 掉
关闭cgo的话 估计会有点好转。
CGO_ENABLED=0 go install -a -v std
然后重新编译你的程序(假设你的程序没有用cgo)。

如果用cgo,那么每个线程就得占用一个pthread线程堆栈,一般是MiB量级,如果不用cgo的话,
每个线程堆栈只需要8KiB (对于Unix系统而言,这个对于Windows应该意义不大)。

创建新的线程应该是由于要做 dns 解析吧,不过限制 86M 时连一次 dns 解析都无法完成有些奇怪,毕竟刚启动时就已经有 6 个线程。
有一个issue是要限制cgo创建的线程的数量,或许以后这个问题会好转。

minux

unread,
Jan 9, 2013, 4:22:58 PM1/9/13
to golang...@googlegroups.com

2013/1/9 steve wang <steve....@gmail.com>
那你的进程崩溃的原因是什么呢?
他说的abort应该是这里:
由于每个线程的堆栈需求过大,所以pthread_create失败了。

steve wang

unread,
Jan 9, 2013, 8:36:02 PM1/9/13
to golang...@googlegroups.com
如果改动代码限制一下goroutine的数量是否可行?我之前在写一个编译工具时需要创建子进程,就是通过手工限制goroutine的数量避免同时创建大量进程。

minux

unread,
Jan 10, 2013, 12:54:14 AM1/10/13
to golang...@googlegroups.com

2013/1/10 steve wang <steve....@gmail.com>
如果改动代码限制一下goroutine的数量是否可行?我之前在写一个编译工具时需要创建子进程,就是通过手工限制goroutine的数量避免同时创建大量进程。
可行啊。不过还是不如系统限制来得方便。 
其实是限制卡在IO上的goroutine数目就可以了。

Yan Unon

unread,
Jan 10, 2013, 12:58:42 AM1/10/13
to golang...@googlegroups.com
Yufei,
我参考minux的建议测试了一下,使用CGO_ENABLED=0能够使得连接数增加时内存不会暴涨,原因应该是减少了每个goroutine的内存占用。
修改MHeapMap_Bits可使程序启动时占用内存降低,修改后程序整体占用10MB左右内存空间,我使用的是tip,修改src/pkg/runtime/malloc.h中的MHeapMap_Bits = 27 - PageShift。



2013/1/10 minux <minu...@gmail.com>
--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
 
 
 

lihui

unread,
Jan 10, 2013, 1:23:46 AM1/10/13
to golang...@googlegroups.com
是不是程序中只要含有使用cgo的包,就所有的goroutine都与c语言的线程一样的占用很大的栈空间,感觉有地儿恐怖。
还是只有某个goroutine调用了有cgo的包才占用较大的栈空间?

2013/1/10 Yan Unon <yan...@gmail.com>

minux

unread,
Jan 10, 2013, 1:30:53 AM1/10/13
to golang...@googlegroups.com

2013/1/10 lihui <ustc....@gmail.com>
是不是程序中只要含有使用cgo的包,就所有的goroutine都与c语言的线程一样的占用很大的栈空间,感觉有地儿恐怖。
还是只有某个goroutine调用了有cgo的包才占用较大的栈空间?
不是goroutine的堆栈 它的堆栈大小跟用不用cgo无关
是OS thread的堆栈,如果用了cgo,那么OS thread的创建必须通过pthread_create进行,
如果自己用clone(2)创建的话无法调用cgo函数(pthread的TCB没初始化),
而pthread创建的thread的堆栈是MiB级的(Linux NPTL pthread实现默认2MiB)。 

Chen Yufei

unread,
Jan 10, 2013, 5:18:37 AM1/10/13
to golang...@googlegroups.com
On 2013年1月10日Thursday at 下午1:54, minux wrote:
>
> 2013/1/10 steve wang <steve....@gmail.com (mailto:steve....@gmail.com)>
> > 如果改动代码限制一下goroutine的数量是否可行?我之前在写一个编译工具时需要创建子进程,就是通过手工限制goroutine的数量避免同时创建大量进程。
>
> 可行啊。不过还是不如系统限制来得方便。
> 其实是限制卡在IO上的goroutine数目就可以了。
>
看了一下 net 库的实现,netFD 的 read/write 用的都是 non-blocking I/O。我的理解是对 TCPConn/UDPConn 之类的读写操作不会因为可能 block 而创建新的线程。

那在使用 net 库的时候应该就只要创建一个 goroutine pool 来做 dns 解析就可以避免创建过多线程了吧?

minux

unread,
Jan 10, 2013, 6:58:14 AM1/10/13
to golang...@googlegroups.com


2013/1/10 Chen Yufei <cyfd...@gmail.com>

On 2013年1月10日Thursday at 下午1:54, minux wrote:
>
> 2013/1/10 steve wang <steve....@gmail.com (mailto:steve....@gmail.com)>
> > 如果改动代码限制一下goroutine的数量是否可行?我之前在写一个编译工具时需要创建子进程,就是通过手工限制goroutine的数量避免同时创建大量进程。
>
> 可行啊。不过还是不如系统限制来得方便。
> 其实是限制卡在IO上的goroutine数目就可以了。
>
看了一下 net 库的实现,netFD 的 read/write 用的都是 non-blocking I/O。我的理解是对 TCPConn/UDPConn 之类的读写操作不会因为可能 block 而创建新的线程。
恩 不过这里fd是non-block IO和我说的卡在IO上的goroutine还有点区别。
Go 1.0.x里面尽管net包的Read/Write是不会阻塞的,但是它并没有利用这点使用的系统调用还是阻塞的版本,
也就是对于调度器来说,还是只要调用Read/Write就认为是进入阻塞IO,如果当时还有goroutine没地方执行,
且满足GOMAXPROCS的约束下就会创建新的OS thread来执行那个goroutine。
Go 1.1引入了syscall.{Read,Write}NB,指明了用于非阻塞IO,解决了这个问题。同时也间接使得网络性能稍
有提升。

Yan Unon

unread,
Jan 8, 2013, 8:27:35 PM1/8/13
to golang...@googlegroups.com
2013/1/9 minux <minu...@gmail.com>

2013/1/8 Yan Unon <yan...@gmail.com>
最近在使用[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同时,我也使用了nodejs版本[shadowsocks-nodejs](https://github.com/clowwindy/shadowsocks-nodejs)测试,并发上百个连接内存占用只有40%左右。
你的OpenVZ系统支持Vswap么?是64位系统么? 
应该支持VSwap,因为我free看到的是256M。64位的Debian6。 

后来看了一下shadowsocks-go的源码,作者对每个连接使用了1个goroutine,然后开启了2个goroutine作为数据双向通道,每个通道缓存只有4kB,也就是128MB内存连75个简单goroutine都撑不起。(不知道这样分析对不对)
没看懂这段,75个goroutine能到128MiB的话,那可是平均下来每个goroutine 1MiB+呀
就是你理解那样,实际情况平均下来每个goroutine 1M+,但是看代码里面并没有大量占用内存的地方,所以对这个现象很疑惑。
IRC: irc.freenode.net #golang-china
@golangchina
 
 
 

Yan Unon

unread,
Jan 8, 2013, 8:30:14 PM1/8/13
to golang...@googlegroups.com

2013/1/9 Monnand <mon...@gmail.com>

On 01/08/2013 10:49 AM, Yan Unon wrote:
不好意思,忘了格式化内容。重发一下。

邮件列表里大家一般都会用纯文本。
嗯,本想方便大家看链接的,结果弄巧成拙,下次注意了。 


最近在使用shadowsocks-go <https://github.com/shadowsocks/shadowsocks-go>
作为翻墙工具,我把服务器端部署在一个内存为128MB的OpenVZ VPS上,但是效果
十分不理想,并发25个连接就把系统内存占满,然后因为不能开辟内存而崩掉。同
时,我也使用了nodejs版本shadowsocks-nodejs
<https://github.com/clowwindy/shadowsocks-nodejs>测试,并发上百个连接内
存占用只有40%左右。


我猜一下,你的VPS上跑的是32位系统吧?
跑的是64位的Debian6。 

minux

unread,
Jan 11, 2013, 3:13:02 AM1/11/13
to golang...@googlegroups.com

2013/1/9 Yan Unon <yan...@gmail.com>
就是你理解那样,实际情况平均下来每个goroutine 1M+,但是看代码里面并没有大量占用内存的地方,所以对这个现象很疑惑。
那你看看是不是线程数很多啊。
(看/proc/PID/task底下的文件夹数就行了)

当然用net/http/pprof的话,有更fancy的做法。。。具体看包文档。

lihui

unread,
Jan 11, 2013, 10:15:41 AM1/11/13
to golang...@googlegroups.com
以前不是说go的底层都用epoll来完成网络IO,让我们能够以通常的程序逻辑来完成nodejs中需要回调才能实现的异步IO吗。怎么还是阻塞版本的系统调用,这样的话goroutine不就没有意义吗?


2013/1/10 minux <minu...@gmail.com>
IRC: irc.freenode.net #golang-china
@golangchina
 
 
 

minux

unread,
Jan 11, 2013, 10:32:52 AM1/11/13
to golang...@googlegroups.com

2013/1/11 lihui <ustc....@gmail.com>
以前不是说go的底层都用epoll来完成网络IO,让我们能够以通常的程序逻辑来完成nodejs中需要回调才能实现的异步IO吗。怎么还是阻塞版本的系统调用,这样的话goroutine不就没有意义吗?
net包所有的网络相关的fd是O_NONBLOCK模式。确实是epoll来等待网络IO。

我前文说的阻塞和非阻塞系统调用是另外一个完全不同的概念。

syscall的系统调用用分两种,一种阻塞的,一种非阻塞的;这是对调度器而言的,非阻塞的系统调用
调度器认为会很快返回,不需要创建新的OS线程以运行其他可运行的goroutine;阻塞的系统调用在
系统调用前会调用runtime·entersyscall(),完成后调用runtime·exitsyscall()来保证如果这个系统调用
阻塞(长时间不返回),那么会创建新的OS线程来运行其他可运行的goroutine。

要再详细讲得长篇大论goroutine的调度了,目前没时间写这个。

tony.jiao

unread,
Jan 16, 2013, 4:39:11 AM1/16/13
to golang...@googlegroups.com
http://jlouisramblings.blogspot.com/2013/01/how-erlang-does-scheduling.html

可以对比看一下erlang的调度

在 2013年1月11日下午11:32,minux <minu...@gmail.com>写道:
目前没时间写这个。


lihui

unread,
Jan 16, 2013, 6:11:38 AM1/16/13
to golang...@googlegroups.com
另外的帖子又谈到cgo了,这个问题没解决,现在我看到cgo就心肝颤。

当网络IO为异步,不考虑文件IO,不考虑因cgo而阻塞的C代码中的情况,那么正常些go代码时会存在什么情况导致 “(如果)这个系统调用阻塞(长时间不返回)”?


2013/1/11 minux <minu...@gmail.com>

--
Reply all
Reply to author
Forward
0 new messages