[英]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.