How to set memory limit to a process in Golang

huangapple go评论81阅读模式
英文:

How to set memory limit to a process in Golang

问题

我使用系统调用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")
	}
}

这是memo的代码:

package main

func main() {
	var a [10000][]int
	for i := 0; i < 1000; i++ {
		a[i] = make([]int, 1024)
	}
}

我创建了大量的内存,并且只为内存设置了10字节的限制,但无论如何它都不会发出段错误信号。

英文:

I use syscall prlimit to set resource limit to process, it works for limit CPU time but when test memory usage, I come with the problem.

package sandbox

import (
	&quot;syscall&quot;
	&quot;unsafe&quot;
)

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
	}
}

and it's my test.

func TestMemoryLimit(t *testing.T) {
	proc, err := os.StartProcess(&quot;test/memo&quot;, []string{&quot;memo&quot;}, &amp;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, &amp;rlimit)
	status, err := proc.Wait()
	if status.Success() {
		t.Fatal(&quot;memory test failed&quot;)
	}
}

this is memo:

package main

func main() {
	var a [10000][]int
	for i := 0; i &lt; 1000; i++ {
		a[i] = make([]int, 1024)
	}
}

I make a large amount of memory and set only 10 bytes for the memory , but it won't signal segment fault signal any way.

答案1

得分: 12

RLIMIT_DATA描述了进程数据段的最大大小。传统上,分配内存的程序通过调用brk()来扩大数据段,从操作系统中分配内存。

Go语言不使用这种方法。相反,它使用mmap()系统调用的变体,在地址空间中请求任意内存区域。这比基于brk()的方法更加灵活,因为你可以使用munmap()释放任意内存区域,而基于brk()的方法只能释放数据段末尾的内存。

由此导致的结果是,RLIMIT_DATA无法有效控制进程使用的内存量。可以尝试使用RLIMIT_AS代替,但要注意,这个限制也包括你用于文件映射的地址空间,特别是在使用共享库的情况下。

英文:

RLIMIT_DATA describes the maximum size of a processes data segment. Traditionally, programs that allocate memory enlarge the data segment with calls to brk() to allocate memory from the operating system.

Go doesn't use this approach. Instead, it uses a variant of the mmap() system call to request regions of memory anywhere in the address space. This is much more flexible than a brk() based approach as you can deallocate arbitrary memory regions with munmap(), whereas a brk() based approach can only deallocate memory from the end of the data segment.

The result of this is that RLIMIT_DATA is inneffective in controlling the amount of memory a process uses. Try using RLIMIT_AS instead, but beware that this limit also incorporates the address space you use for file mappings, especially in the case of shared libraries.

答案2

得分: 1

有一个提案软内存限制,可能在 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 程序始终充分利用其内存限制,在某些情况下提高资源效率。

以下是关于“内存限制”使用的一些建议,参考自《Go 垃圾回收器指南》:

虽然内存限制是一个强大的工具,Go 运行时会采取措施来减轻滥用的最坏行为,但仍然需要谨慎使用。以下是关于内存限制最有用和适用的地方以及可能带来更多伤害的地方的一些建议。

  • 当你的 Go 程序的执行环境完全在你的控制下,并且 Go 程序是唯一可以访问某些资源集合的程序时,可以充分利用内存限制。一个很好的例子是将 Web 服务部署到具有固定可用内存量的容器中。在这种情况下,一个经验法则是留出额外的 5-10% 的余地,以考虑 Go 运行时不知道的内存来源。

  • 可以随时调整内存限制以适应变化的条件。一个很好的例子是 cgo 程序,其中 C 库临时需要使用更多的内存。

  • 如果 Go 程序可能与其他程序共享一些有限的内存,并且这些程序通常与 Go 程序解耦,不要在内存限制下将 GOGC 设置为 off。相反,保留内存限制,因为它可能有助于遏制不希望的瞬态行为,但对于平均情况,将 GOGC 设置为一些较小的合理值。尽管试图为共享租户程序“保留”内存可能很诱人,但除非程序完全同步(例如,Go 程序调用某个子进程并在其被调用者执行时阻塞),否则结果将不太可靠,因为不可避免地两个程序都需要更多的内存。当 Go 程序不需要时,让它使用更少的内存将产生更可靠的结果。这个建议也适用于超额提交的情况,其中在一台机器上运行的容器的内存限制之和可能超过机器实际可用的物理内存。

  • 在部署到一个你无法控制的执行环境时,尤其是当程序的内存使用与其输入成比例时,不要使用内存限制。一个很好的例子是命令行工具或桌面应用程序。当不清楚可能输入什么样的输入或系统上可能有多少可用内存时,将内存限制嵌入程序中可能导致令人困惑的崩溃和性能不佳。此外,高级终端用户始终可以设置内存限制。

  • 当程序已接近其环境的内存限制时,不要设置内存限制以避免内存不足的情况。这实际上将一个内存不足的风险替换为严重的应用程序减速的风险,即使 Go 会努力减轻抖动的影响,这通常也不是一个有利的交换。在这种情况下,增加环境的内存限制(然后可能设置内存限制)或减小 GOGC(提供比抖动减轻更清晰的权衡)将更加有效。

英文:

There is one proposal Soft memory limit, it may be released after go 1.18

> This option comes in two flavors: a new runtime/debug function called SetMemoryLimit and a GOMEMLIMIT environment variable. In sum, the runtime will try to maintain this memory limit by limiting the size of the heap, and by returning memory to the underlying platform more aggressively. This includes with a mechanism to help mitigate garbage collection death spirals. Finally, by setting GOGC=off, the Go runtime will always grow the heap to the full memory limit.

> This new option gives applications better control over their resource economy. It empowers users to:

> - Better utilize the memory that they already have,
> - Confidently decrease their memory limits, knowing Go will respect them,
> - Avoid unsupported forms of garbage collection tuning.


Update

This feature will be released in Go 1.19

> The runtime now includes support for a soft memory limit. This memory limit includes the Go heap and all other memory managed by the runtime, and excludes external memory sources such as mappings of the binary itself, memory managed in other languages, and memory held by the operating system on behalf of the Go program.

This limit may be managed via runtime/debug.SetMemoryLimit or the equivalent GOMEMLIMIT environment variable.

The limit works in conjunction with runtime/debug.SetGCPercent / GOGC, and will be respected even if GOGC=off, allowing Go programs to always make maximal use of their memory limit, improving resource efficiency in some cases.


Here are some suggestions about the memory limit usage per A Guide to the Go Garbage Collector

While the memory limit is a powerful tool, and the Go runtime takes steps to mitigate the worst behaviors from misuse, it's still important to use it thoughtfully. Below is a collection of tidbits of advice about where the memory limit is most useful and applicable, and where it might cause more harm than good.

  • Do take advantage of the memory limit when the execution environment of your Go program is entirely within your control, and the Go program is the only program with access to some set of resources (i.e. some kind of memory reservation, like a container memory limit).

    A good example is the deployment of a web service into containers with a fixed amount of available memory.

    In this case, a good rule of thumb is to leave an additional 5-10% of headroom to account for memory sources the Go runtime is unaware of.

  • Do feel free to adjust the memory limit in real time to adapt to changing conditions.

    A good example is a cgo program where C libraries temporarily need to use substantially more memory.

  • Don't set GOGC to off with a memory limit if the Go program might share some of its limited memory with other programs, and those programs are generally decoupled from the Go program. Instead, keep the memory limit since it may help to curb undesirable transient behavior, but set GOGC to some smaller, reasonable value for the average case.

    While it may be tempting to try and "reserve" memory for co-tenant programs, unless the programs are fully synchronized (e.g. the Go program calls some subprocess and blocks while its callee executes), the result will be less reliable as inevitably both programs will need more memory. Letting the Go program use less memory when it doesn't need it will generate a more reliable result overall. This advice also applies to overcommit situations, where the sum of memory limits of containers running on one machine may exceed the actual physical memory available to the machine.

  • Don't use the memory limit when deploying to an execution environment you don't control, especially when your program's memory use is proportional to its inputs.

    A good example is a CLI tool or a desktop application. Baking a memory limit into the program when it's unclear what kind of inputs it might be fed, or how much memory might be available on the system can lead to confusing crashes and poor performance. Plus, an advanced end-user can always set a memory limit if they wish.

  • Don't set a memory limit to avoid out-of-memory conditions when a program is already close to its environment's memory limits.

    This effectively replaces an out-of-memory risk with a risk of severe application slowdown, which is often not a favorable trade, even with the efforts Go makes to mitigate thrashing. In such a case, it would be much more effective to either increase the environment's memory limits (and then potentially set a memory limit) or decrease GOGC (which provides a much cleaner trade-off than thrashing-mitigation does).

huangapple
  • 本文由 发表于 2014年7月2日 12:07:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/24522692.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定