首页 > 解决方案 > 在 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 个字节。那么如何在函数体内返回一个函数和很多东西呢?这是一种有效的方法吗?有什么缺点?

标签: functiongocompiler-construction

解决方案


如果你不习惯闭包,它们可能看起来有点神奇。但是,它们很容易在编译器中实现:

  • 编译器查找必须由闭包捕获的任何变量。它将它们放入一个工作区,只要闭包本身存在,该工作区就会被分配并保持分配状态。

  • 然后编译器生成带有秘密额外参数的内部函数,或一些其他运行时技巧,1以便调用该函数激活闭包。

因为返回的函数通过编译时安排访问其闭包变量,所以没有什么特别需要的。由于 Go 是一种垃圾收集语言,因此也不需要其他任何东西:指向闭包的指针使闭包数据保持活动状态,直到指针消失,因为无法再调用函数,此时闭包数据消失(嗯,在下一次 GC 时)。


1 GCC 有时使用trampolines为 C 执行此操作,其中 trampolines 是在运行时生成的可执行代码。可执行代码可以设置寄存器或传递额外参数等。这可能会很昂贵,因为在运行时被视为数据的东西(生成的代码)必须在运行时转换为可执行代码(可能需要系统调用并可能需要一些监督代码“审查”生成的运行时代码)。

Go不需要任何这些,因为该语言在定义时考虑到了闭包,因此实现者不会,呃,“关闭”任何简单的方法来使这一切工作。一些运行时 ABI 的定义也考虑到了闭包,例如,r1在所有指向函数的指针类型中,寄存器被保留为闭包变量指针,或者一些类似的类型。


推荐阅读