Memory management 将指针设置为nil以防止Golang中的内存泄漏
我正在学习围棋,作为练习,我想实现一个链表。作为参考,我查看了官方的Go代码()。有一件事让我难以忘怀,那就是这些台词:Memory management 将指针设置为nil以防止Golang中的内存泄漏,memory-management,go,memory-leaks,garbage-collection,memory-profiling,Memory Management,Go,Memory Leaks,Garbage Collection,Memory Profiling,我正在学习围棋,作为练习,我想实现一个链表。作为参考,我查看了官方的Go代码()。有一件事让我难以忘怀,那就是这些台词: 108 // remove removes e from its list, decrements l.len, and returns e. 109 func (l *List) remove(e *Element) *Element { 110 e.prev.next = e.next 111 e.next.prev = e
108 // remove removes e from its list, decrements l.len, and returns e.
109 func (l *List) remove(e *Element) *Element {
110 e.prev.next = e.next
111 e.next.prev = e.prev
112 e.next = nil // avoid memory leaks
113 e.prev = nil // avoid memory leaks
114 e.list = nil
115 l.len--
116 return e
117 }
我很好奇,在这种情况下,如何将指针设置为nil来防止内存泄漏?如果可能的话,我想构造一个有此缺陷的程序,并在使用pprof进行评测时看到它(我将使用修改后的list.go版本,而不使用此nil指针设置)
为清楚起见:如果其中一个节点具有指向它的外部指针,则所有相邻的已删除节点都将具有通过该指针的活动引用,并且不会被删除。
Golang垃圾收集器基于三色标记和扫描算法。 简而言之,程序使用的每个内存都与一种颜色相关联。颜色决定了内存是否应该被丢弃 如果某个内存未被引用(直接或间接),此算法将标记要释放的内存。但如果我们看一下代码:
e.prev.next = e.next
e.next.prev = e.prev
这会将e.next中的指针复制到e.prev.next中。现在,假设您想通过一个新的完全创建的元素更新e.prev.next
之前删除的元素不会被丢弃,因为它仍然被e.next引用
这就是为什么会有这些行:
e.next = nil // avoid memory leaks
e.prev = nil // avoid memory leaks
这可以防止留下旧引用,从而防止内存泄漏。您的假设是正确的。如果有一组指针相互指向,但没有指向该组任何成员的引用/指针,则垃圾收集器将检测到该组不可访问,并将正确释放该组 但对内存泄漏的解释很简单。我们可以从列表中获取包装器,其中包含未报告的
元素.next
和元素.prev
指向列表中下一个和上一个元素的指针
如果这些指针未设置为nil
,则从列表中删除元素时,它们将保留对下一个和上一个元素包装器的引用,包括与这些元素关联的值
请参见此示例:
var e2 *list.Element
func main() {
listTest()
fmt.Println(e2.Value)
// At this point we expect everything from the list to be
// garbage collected at any time, we only have reference to e2.
// If e2.prev and e2.next would not be set to nil,
// e1 and e3 could not be freed!
}
func listTest() {
l := list.New()
e1 := l.PushBack(1)
e2 = l.PushBack(2)
e3 := l.PushBack(3)
// List is now [1, 2, 3]
fmt.Println(e1.Value, e2.Value, e3.Value)
l.Remove(e2)
// Now list is [1, 3], it does not contain e2
}
在listest()
中,我们构建了一个包含3个元素的列表,并将第2个元素存储在一个全局变量e2
中。然后我们移除这个元素。现在我们希望,除了e2
(以及包装在其中的值)之外,当listTest()
返回时,所有其他内容都会被垃圾收集,因为在listTest()
函数之外无法访问该列表。是的,我们在e2
中有一个指向元素的指针,但是e2
已经(应该)与列表没有任何关系,因为我们删除了它
如果
e2
中的prev
和next
指针未设置为nil
,则它们所指向的元素中包装的值将永远无法递归释放。但是由于正确地将它们设置为nil
,在上面的示例中,e1
和e3
——以及包装在其中的值——将被释放(在下一次垃圾收集运行时)。我在上面添加了一条注释,解释了我认为会发生什么。我不确定你所说的“删除的元素仍然被e.next引用”是什么意思,e不是删除的元素吗?我用你描述的图片回答了这个问题,如果我错了,请纠正我。实际上,我用一个修改过的(带有内存泄漏错误)版本的列表来尝试这个方法,我可以看到它没有释放内存。