当在Golang中返回一个函数时,内存分配是如何进行的?

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

What is the memory allocation when you return a function in Golang?

问题

这是一个简化的代码:

func MyHandler(a int) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteCode(a)
    })
}

每当有一个HTTP请求到来时,将调用MyHandler函数,并返回一个函数来处理该请求。因此,每当有一个HTTP请求到来时,都会创建一个新的函数对象。在Go语言中,函数是一等公民。我试图理解从内存的角度来看,当你返回一个函数时,实际上发生了什么。当你返回一个值时,比如一个整数,在栈中会占用4个字节。那么返回一个函数和函数体内的许多东西呢?这种方式是否高效?有什么缺点吗?

英文:

Here is a simplified code

func MyHandler(a int) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteCode(a)
    })
}

Whenever a http request comes MyHandler will be called, and it will return a function which will be used to handle the request. So whenever a http request comes a new function object will be created. Function is taken as the first class in Go. I'm trying to understand what actually happened when you return a function from memory's perspective. When you return a value, for example an integer, it will occupy 4 bytes in stack. So how about return a function and lots of things inside the function body? Is it an efficient way to do so? What's shortcomings?

答案1

得分: 3

如果你不熟悉闭包,它们可能看起来有点神奇。然而,在编译器中实现闭包是很容易的:

  • 编译器会找出所有必须被闭包“捕获”的变量。它将它们放入一个工作区,该工作区将在闭包存在期间被分配和保留。

  • 然后,编译器会生成内部函数,并添加一个秘密的额外参数,或者使用其他运行时技巧,以便在调用函数时激活闭包。

由于返回的函数通过编译时的安排访问其闭包变量,所以不需要任何特殊处理。而且,由于Go是一种垃圾回收语言,也不需要其他任何操作:闭包的指针会保持闭包数据的存活,直到指针消失,因为函数无法再被调用,此时闭包数据会消失(嗯,在下一次垃圾回收时)。


1GCC有时会为C语言使用跳板来实现这一点,其中跳板是在运行时生成的可执行代码。可执行代码可以设置一个寄存器、传递一个额外的参数或者进行其他操作。这可能是昂贵的,因为在运行时将被视为数据的东西(生成的代码)必须在运行时转换为可执行代码(可能需要系统调用,并且可能需要一些监管代码来“审核”生成的运行时代码)。

Go不需要这些操作,因为该语言在设计时就考虑了闭包,所以实现者们没有限制任何使这一切工作的简单方法。一些运行时ABI也考虑了闭包,例如,所有指向函数类型的指针中都将寄存器r1保留为闭包变量指针,或者类似的处理方式。

英文:

If you're not used to closures, they may seem a bit magic. They are, however, easy to implement in compilers:

  • The compiler finds any variables that must be captured by the closure. It puts them into a working area that will be allocated and remain allocated as long as the closure itself exists.

  • The compiler then generates the inner function with a secret extra parameter, or some other runtime trickery,<sup>1</sup> such that calling the function activates the closure.

Because the returned function accesses its closure variables through the compile-time arrangement, there's nothing special needed. And since Go is a garbage-collected language, there's nothing else needed either: the pointer to the closure keeps the closure data alive until the pointer is gone because the function cannot be called any more, at which point the closure data evaporates (well, at the next GC).


<sup>1</sup>GCC sometimes uses trampolines to do this for C, where trampolines are executable code generated at runtime. The executable code may set a register or pass an extra parameter or some such. This can be expensive since something treated as data at runtime (generated code) must be turned into executable code at runtime (possibly requiring a system call and potentially requiring that some supervisory code "vet" the resulting runtime code).

Go does not need any of this because the language was defined with closures in mind, so implementors don't, er, "close off" any easy ways to make this all work. Some runtime ABIs are defined with closures in mind as well, e.g., register r1 is reserved as the closure-variables pointer in all pointer-to-function types, or some such.

答案2

得分: 1

实际函数大小是无关紧要的。当你返回这样一个函数时,内存将被分配给闭包,也就是函数使用的作用域中的任何变量。在这种情况下,将返回一个指针,其中包含函数的地址和指向闭包的指针,闭包中将包含对变量 a 的引用。

英文:

Actual function size is irrelevant. When you return a function like this, memory will be allocated for the closure, that is, any variables in the scope that the function uses. In this case, a pointer will be returned containing the address of the function and a pointer to the closure, which will contain a reference to the variable a.

huangapple
  • 本文由 发表于 2021年9月8日 10:20:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/69096255.html
匿名

发表评论

匿名网友

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

确定