英文:
Conceptual memory model of the Go language
问题
我将为您翻译以下内容:
我有一个问题要问Go程序员。我找不到关于Go内存模型细节的信息。我将尝试描述我的问题:
如果我在C中声明和定义一个变量或数组,我知道它在RAM中的确切位置有一个特定的地址,直到我执行会改变这个位置的操作。
但在Python中情况并非如此:Python为我处理整个内存管理,我无法确定我的数据是否始终位于同一个位置。例如,字符串在实践中是不可变的,尽管语言表明它们是可变的。这使得在处理敏感数据时编写安全的程序几乎不可能(或者至少非常不实际)。
我的问题是:从这个角度来看,Go是如何工作的?它更像C还是Python?还是完全不同?在Go中是否可以像在C中那样轻松处理敏感数据?
英文:
I've a question to the Go programmers amongst you. I can't find information about the details of the memory model of Go. I shall try to describe my problem:
If I declare and define a variable or array in C, I know that this exists somewhere in the RAM at exactly one position with one specific address, until I do something that will change this.
This is not the case in Python: Python does the whole memory management for me, I can't be sure that my data will be located always at one position. E.g. strings are in practice immutable, even though the language suggests that they is not. This makes secure programming with sensitive data nearly impossible (or at least very impractical).
My question is: How does Go work from this perspective. Is it more like C or like Python? Or is it completely different? Is it possible to handle sensitive data as comfortably as in C?
答案1
得分: 6
注意:如在“Go Slices: usage and internals”中所述:
[4]int
的内存表示只是按顺序排列的四个整数值:
-
Go 的数组是值。
-
数组变量表示整个数组;它不是指向第一个数组元素的指针(这在 C 中是这样的)。这意味着当你赋值或传递一个数组值时,你将复制它的内容。(为了避免复制,你可以传递一个指向数组的指针,但那是指向数组的指针,而不是数组本身。)
-
一种思考数组的方式是将其视为一种类似结构体的东西,但其字段是按索引而不是按名称命名的:一个固定大小的复合值。
如果你正在看“指向数组的指针”,那么你需要一个切片。
-
切片是数组段的描述符。它由指向数组的指针、段的长度和容量(段的最大长度)组成。
-
我们之前用
make([]byte, 5)
创建的变量s
的结构如下:
关于数组和切片的内存分配方式,你可以查看“Effective Go”。
-
Go 有两个分配原语,内置函数
new
和make
。
它们执行不同的操作,适用于不同的类型,这可能会让人感到困惑,但规则很简单。 -
首先让我们谈谈
new
。它是一个内置函数,用于分配内存,但与其他一些语言中的同名函数不同,它不会初始化内存,只会将其清零。
也就是说,new(T)
为类型T
的新项分配了一个零值的存储空间,并返回其地址,即类型为*T
的值。
按照 Go 的术语,它返回一个指向新分配的类型T
的零值的指针。 -
使用
make
进行分配:内置函数make(T, args)
的目的与new(T)
不同。它只创建切片、映射和通道,并返回一个已初始化(而不是零值)的类型T
的值(而不是*T
)。
区分它们的原因是,这三种类型在底层表示的是对在使用之前必须初始化的数据结构的引用。
例如,切片是一个三项描述符,包含指向数据(在数组内部)的指针、长度和容量,直到这些项被初始化之前,切片为 nil。
更多信息请参见“Understanding Pointers and Memory Allocation”。
英文:
Note: as mention in "Go Slices: usage and internals":
> The in-memory representation of [4]int
is just four integer values laid out sequentially:
> Go's arrays are values.
> An array variable denotes the entire array; it is not a pointer to the first array element (as would be the case in C). This means that when you assign or pass around an array value you will make a copy of its contents.
(To avoid the copy you could pass a pointer to the array, but then that's a pointer to an array, not an array.)
> One way to think about arrays is as a sort of struct but with indexed rather than named fields: a fixed-size composite value.
If you are looking at "a pointer to an array", then you would need a slice.
> A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).
> Our variable s
, created earlier by make([]byte, 5)
, is structured like this:
For the way memory allocation work for array and slice, you can check out "Effective Go"
> Go has two allocation primitives, the built-in functions new
and make
.
They do different things and apply to different types, which can be confusing, but the rules are simple.
> - Let's talk about new first. It's a built-in function that allocates memory, but unlike its namesakes in some other languages it does not initialize the memory, it only zeros it.
That is, new(T)
allocates zeroed storage for a new item of type T
and returns its address, a value of type *T
.
In Go terminology, it returns a pointer to a newly allocated zero value of type T
.
> - allocation with make
: The built-in function make(T, args)
serves a purpose different from new(T)
. It creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T
(not *T
).
The reason for the distinction is that these three types represent, under the covers, references to data structures that must be initialized before use.
A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the length, and the capacity, and until those items are initialized, the slice is nil.
See more at "Understanding Pointers and Memory Allocation".
答案2
得分: 2
Go语言中的内存处理方式介于C语言和Python语言之间。与Python相比,Go语言对内存布局的控制更多,几乎与C语言一样多。但是,Go语言像Python一样具有垃圾回收功能,因此无法显式地控制内存的分配和释放。
在Go语言中,有时会自动移动内存。当一个goroutine的堆栈满了时,它会被复制到一个更大的堆栈中。也许将来Go语言也会像Java一样拥有复制垃圾回收器,但目前还没有。
因此,在Go语言中确保敏感数据不会留在内存中可能比在C语言中更困难。但在具有虚拟内存的系统中(即所有现代计算机),您的进程总是有可能被交换出去,其他进程会获取其内存。也许操作系统会首先将内存清零,也许不会。
但是在Go语言中,将敏感值留在内存中并不像在C语言中那样危险。Go语言不允许您像C语言的数组那样访问切片之外的内存(这就是导致Heartbleed漏洞的原因)。而且,在Go语言中分配新的内存块时,它会被清零,因此不会发生泄漏。
英文:
The way memory is handled in Go is in between how it works in C and how it works in Python. Go gives a lot more control over memory layout than Python does—about as much as C. But Go is garbage-collected, like Python. So you don't have explicit control over the allocation and deallocation of memory.
Sometimes memory in Go can be moved around automatically. When a goroutine's stack gets full, it is copied to a larger one. Some day Go may have a copying garbage collector like Java, too. (It doesn't right now.)
So making sure sensitive data isn't left lying around in memory is probably harder in Go than in C. But in a system with virtual memory (i.e., all modern computers), there's always the possibility that your process will be swapped out and some other process will get its memory. Maybe the OS zeroes the memory first, maybe it doesn't.
But in Go, leaving sensitive values in memory is not nearly as dangerous as it is in C. Go doesn't let you access memory outside the bounds of a slice like C does with its arrays. (This is what led to the Heartbleed bug.) And when you allocate a new chunk of memory in Go, it is zeroed, so nothing can leak that way.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论