[英]How to set memory limit to a process in Golang
我使用 syscall prlimit 来设置进程的资源限制,它适用于限制 CPU 时间,但是在测试内存使用情况时,我遇到了问题。
package sandbox
import (
"syscall"
"unsafe"
)
func prLimit(pid int, limit uintptr, rlimit *syscall.Rlimit) error {
_, _, errno := syscall.RawSyscall6(syscall.SYS_PRLIMIT64, uintptr(pid), limit, uintptr(unsafe.Pointer(rlimit)), 0, 0, 0)
var err error
if errno != 0 {
err = errno
return err
} else {
return nil
}
}
这是我的测试。
func TestMemoryLimit(t *testing.T) {
proc, err := os.StartProcess("test/memo", []string{"memo"}, &os.ProcAttr{})
if err != nil {
panic(err)
}
defer proc.Kill()
var rlimit syscall.Rlimit
rlimit.Cur = 10
rlimit.Max = 10 + 1024
prLimit(proc.Pid, syscall.RLIMIT_DATA, &rlimit)
status, err := proc.Wait()
if status.Success() {
t.Fatal("memory test failed")
}
}
这是备忘录:
package main
func main() {
var a [10000][]int
for i := 0; i < 1000; i++ {
a[i] = make([]int, 1024)
}
}
我做了大量的内存,只为内存设置了 10 个字节,但它不会以任何方式发出段错误信号。
RLIMIT_DATA
描述进程数据段的最大大小。 传统上,分配内存的程序通过调用brk()
从操作系统分配内存来扩大数据段。
Go 不使用这种方法。 相反,它使用mmap()
系统调用的变体来请求地址空间中任何位置的内存区域。 这比基于brk()
的方法灵活得多,因为您可以使用munmap()
释放任意内存区域,而基于brk()
的方法只能从数据段的末尾释放内存。
其结果是RLIMIT_DATA
在控制进程使用的内存量方面无效。 请尝试改用RLIMIT_AS
,但请注意,此限制还包含用于文件映射的地址空间,尤其是在共享库的情况下。
有一个提议Soft memory limit ,可能会在 go 1.18 之后释放
此选项有两种形式:一个名为
SetMemoryLimit
的新runtime/debug
函数和一个GOMEMLIMIT
环境变量。 总之,运行时将尝试通过限制堆的大小以及更积极地将内存返回给底层平台来维持这个内存限制。 这包括帮助减轻垃圾收集死亡螺旋的机制。 最后,通过设置 GOGC=off,Go 运行时将始终将堆增长到完整内存限制。
这个新选项使应用程序可以更好地控制其资源经济性。 它使用户能够:
- 更好地利用他们已有的内存,
- 自信地减少他们的记忆限制,知道 Go 会尊重他们,
- 避免不支持的垃圾收集调整形式。
更新
此功能将在Go 1.19中发布
运行时现在包括对软内存限制的支持。 此内存限制包括 Go 堆和运行时管理的所有其他内存,不包括外部内存源,例如二进制文件本身的映射、以其他语言管理的内存以及操作系统代表 Go 程序持有的内存。
此限制可以通过runtime/debug.SetMemoryLimit
或等效的GOMEMLIMIT
环境变量进行管理。
该限制与runtime/debug.SetGCPercent
/ GOGC
结合使用,即使GOGC=off
也会受到尊重,从而允许 Go 程序始终最大限度地利用其内存限制,在某些情况下提高资源效率。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.