golang 转到片段以观察运行时行为和内存分配

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang 转到片段以观察运行时行为和内存分配相关的知识,希望对你有一定的参考价值。

// This snippet is helpful for observing OS threads as the runtime is creating
// blocked out of the gate goroutines. These should immediately be going off of
// CPU and remaining this way for the entirety of the process's lifetime.
package main

import (
	"fmt"
	"os"
	"runtime"
	"time"

	"net/http"
	_ "net/http/pprof"
)

func main() {
	const limit = 100000
	go func() {
		fmt.Println(http.ListenAndServe(":9876", nil))
	}()

	for i := 0; i < limit; i++ {

		c := make(chan struct{})
		go func() {
			<-c
		}()
	}

	fmt.Fprintf(os.Stdout, "Number of goroutines: %d\n", runtime.NumGoroutine())
	<-time.NewTimer(600 * time.Second).C
}
// This snippet is helpful for observing OS threads as the runtime is creating
// blocked out of the gate goroutines. These should immediately be going off of
// CPU and remaining this way for the entirety of the process's lifetime.
package main

import (
	"fmt"
	"os"
	"runtime"
	"time"
)

func main() {
	for {
		fmt.Fprintf(os.Stderr, "%d ", runtime.NumGoroutine())
		c := make(chan struct{})
		go func() {
			<-c
		}()
		<-time.NewTimer(time.Second).C
	}
}

malloc 在分配内存时是不是保留更多空间?

【中文标题】malloc 在分配内存时是不是保留更多空间?【英文标题】:Does malloc reserve more space while allocating memory?malloc 在分配内存时是否保留更多空间? 【发布时间】:2019-08-13 04:00:31 【问题描述】:

我在我的测试程序中观察到以下行为:

我正在为 1 MB 执行 malloc(),然后在 sleep(10) 之后执行 free()。我这样做了五次。我正在观察程序运行时top 的内存消耗。

一旦free()-d,我预计程序的虚拟内存 (VIRT) 消耗将减少 1 MB。但实际上并非如此。它保持稳定。这种行为的解释是什么? malloc() 在分配内存时会做一些保留吗?

【问题讨论】:

相关:How do I free memory in C? Memory usage doesn't decrease when free() used的可能重复 @Useless 这个问题比旧问题有更好的答案,所以我打破惯例,将旧问题标记为这个问题的副本。 我认为几乎所有 malloc/free 实现都使用一些内部管理,这些管理确实请求更大的块并机会主义地释放它们。这可以使用brk(2) 或 mmap。这也意味着页面在被触摸之前可能实际上并没有被使用(有时甚至在空闲时未提交,因此虚拟或数据段大小并不那么重要) 【参考方案1】:

一旦free()-d,我预计程序的虚拟内存 (VIRT) 消耗将减少 1MB。

嗯,C 标准不能保证这一点。它只是说,一旦你free() 内存,你就不应该再访问它了。

内存块是实际返回到可用内存池还是保留以供将来分配由内存管理器决定。

【讨论】:

是否可以将 free() 的内存块释放回操作系统? @user1228352 不,C 语言不允许这样做。如果你想要更多的控制,你需要实现你自己的内存管理器,它依赖于平台特定的操作系统系统调用。 @user1228352 我理解这之后的感觉,让我们说诡计,但是 - 你真的不想走那条路,从长远来看也没有意义,因为它只是浪费是时候弄清楚如何制作自己的内存管理器(如果操作系统允许)并对其进行调试。遵循 C 标准,您将获得更舒适的体验,而操作系统则完成了它的目标。好吧,除非您的目标是制作自己的操作系统,否则您可能不会问这个问题。 @user1228352 为什么要这样做?虚拟内存实际上是免费的。 为什么要减少对非稀缺物品的不必要消耗?如果您想要一个有用的答案,您应该告诉我们更多关于您的环境的信息。一些不寻常的环境也有不寻常的mallocfree 实现。如果你有一个真正的问题(这不仅仅是装饰性的),你可以用一个永远不会拥有任何额外虚拟内存的分配器来替换分配器,但是由于碎片等问题,它有大约 99% 的机会只会让事情变得更糟。跨度> 【参考方案2】:

C 标准不强制mallocfree 的实现者直接将内存返回给操作系统。所以不同的 C 库实现会有不同的行为。他们中的一些人可能会直接还给它,而有些人可能不会。事实上,相同的实现也会因分配大小和模式而有所不同。

这种行为当然是有充分理由的:

    这并不总是可能的。操作系统级别的内存分配通常在页面中完成(一次 4KB、4MB 或 ... 大小)。如果在释放另一部分后页面的一小部分仍在使用,则在该部分也被释放之前,该页面无法返回给操作系统。 效率。应用程序很可能会再次请求内存。那么为什么要把它还给操作系统并在不久之后再次要求它。 (当然,保留的内存大小可能有限制。)

在大多数情况下,如果实现决定保留它(假设它是一个很好的实现),那么您无需为 free 的内存负责。迟早它会被重新分配或返回给操作系统。因此,内存使用的优化应该基于你拥有malloc-ed 而你没有free-d 的数量。您必须担心的情况是,当您的分配模式/大小开始导致内存碎片时,这本身就是一个非常大的话题。

但是,如果您使用的是嵌入式系统并且可用内存量有限,并且您需要更多地控制何时/如何分配和释放内存,那么您需要直接从操作系统请求内存页面并进行管理手动。

编辑:我没有解释为什么你不对你空闲的内存负责。 原因是,在现代操作系统上,分配的内存是虚拟的。这意味着如果您在 32 位系统或 10TB 的 64 位系统上分配 512MB,只要您不读取或写入该内存,它就不会为它保留任何物理空间。实际上,它只会为您从那个大块而不是整个块中触摸的页面保留物理内存。在“一段时间不使用该内存”之后,其内容将被复制到磁盘,而底层物理内存将用于其他用途。

【讨论】:

请注意,某些分配器可能会通过使用操作系统特定的调用来避免将数据复制到磁盘的可能性,这些调用会说“这些页面没有被使用,所以请随意删除它们的内容,即使我不释放虚拟内存本身”。例如,在 Linux 上使用 madvise 调用和 MADV_DONTNEED【参考方案3】:

这在很大程度上取决于实际使用的 malloc 实现。

在 Linux 下,有一个阈值 (MMAP_THRESHOLD) 来决定给定 malloc() 请求的内存来自哪里。

如果请求的数量小于或等于MMAP_THRESHOLD,则通过从所谓的“空闲列表”中取出它来满足请求,如果任何内存块已经是free()d。否则,程序的"break line"(即数据段的结尾)会增加,并且此进程为程序提供的内存用于请求。

free() 上,释放的内存块被添加到空闲列表中。如果在数据段的最后有足够的空闲内存,则再次移动断线(上面提到)以缩小数据段,将多余的内存返回给操作系统。

如果请求的数量超过MMAP_THRESHOLD,操作系统会请求一个单独的内存块并在free()期间再次返回。

详情请参阅https://linux.die.net/man/3/malloc。

【讨论】:

以上是关于golang 转到片段以观察运行时行为和内存分配的主要内容,如果未能解决你的问题,请参考以下文章

golang反射自定义tag

Android Fragment 和 Activity 在方向更改时的行为

Golang内部构件,第6部分:自举和内存分配器初始化

导航抽屉,处理后退按钮以转到以前的片段?

手撸golang 行为型设计模式 观察者模式

golang/go语言Go语言之反射