OpenVZ VPS上内存占用过多
yanunon opened this issue · comments
在内存为128M、系统为64bit Debian6的OpenVZ VPS上只能保持25个左右的连接,每个连接使用了3个goroutine,算下来每个goroutine占用了1MB+的内存。
连接大于25个时,不能开辟新的内存空间,程序崩溃。
OpenVZ 限制 virtual memory 的占用,Go runtime 会预先 allocate 一大块 virtual memory 供以后使用。
golang-nuts 上的讨论
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/4Y-SO5QnGyY
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
@clowwindy 提到可以试一下用 ulimit 限制内存使用。
我会试一下 minux 和 @clowwindy 提到的方法,看看究竟怎么解决这个问题。
ulimit -Sv 90000 的时候 shadowsocks-go 能够启动,再小的话启动会失败。
但设置 limit 之后程序运行会出奇怪的错误,shadowsocks-server DNS 解析会出错。
还是要试试看改 VM 分配的方法。
根据 minux 和 @clowwindy 的建议,暂时考虑下面的解决方案:
- 发布二进制文件时
- 修改
$GOROOT/src/pkg/runtime/malloc.h
,重新编译 go compiler 后再创建二进制文件。这可以避免程序刚启动时 OpenVZ 统计到占用大量内存 - 编译时禁用 cgo,
CGO_ENABLED=0 go install -a -v std
,这样 Go runtime 可以使用 clone(2) 来创建线程,每个线程只需要 8KB 内存(Linux NPTL pthread_create(3) 创建的线程堆栈默认为 2MB) - 使用 Go 1.1 (应该快 release 了,在这之前可以看下 tip 是否稳定),由于引入非阻塞的 read/write
syscall.{Read, Write}NB
,网络性能应该稍有提升
- 修改
- 修改代码,用 goroutine pool 来做 dns 解析,限制线程数量
- 如果将来 Go 解决了线程数过多的问题可能就不需要这个优化了
更多信息 https://groups.google.com/forum/?fromgroups=#!topic/golang-china/VXUI8ddxeCY
最近比较忙,得稍等一段时间才能 fix。
多谢~
禁用 cgo 后,go 会使用自带的 name resolution library,从而不会发生创建过多线程的问题。
使用 shadowsocks-httpget 创建 1000 个并发连接,不使用 dnspool,server 运行在 64bit Debian 6 上测试 get 本地 nginx 的静态页面,测试结果
- 禁用 cgo,最多有 8 个 shadowsocks-server 进程,VIRT 333M,RSS 75M
- 启用 cgo,线程数达到 53,VIRT 3.9G,RSS 84M
或许编译时直接禁用 cgo 是更简单有效的解决方案,不过要看禁用 cgo 后 dns 解析是否会有什么问题。
修改了src/pkg/runtime/malloc.h,会导致测试失败,但是程序仍能正常运行,可能有隐藏的问题,毕竟测试没有完全通过。
直接禁用cgo的确是目前比较合适的方法。
在支持 vswap 的 OpenVZ 主机上测试过,不会出现错误统计内存占用的问题。只是启动 shadowsocks-go 大概占用 2M 内存。
我会在 README 中加入相关说明。
咨询下现在官方的binary是怎么编译出来的呀