将指针设置为零以防止Golang中的内存泄漏 [英] Setting pointers to nil to prevent memory leak in Golang

查看:397
本文介绍了将指针设置为零以防止Golang中的内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在学Go,作为一个练习,我想实现一个链表。作为参考,我看了官方的Go代码(


  1. 我们创建一个指向Node2的外部指针

  2. 我们从列表中删除节点2-4

  3. 在这一点上,您只能期望节点1,2&
    5是活着的,其余的则是GC-ed。但是,由于Node2仍然
    指向Node3&等等,整个链仍然未收集。 你的假设是正确的。如果有一组指针指向对方,但没有引用/指向此组的任何成员,则该组将被垃圾收集器检测为无法访问,并且将被正确释放。



    但是对内存泄漏的解释很简单。我们可以获取 list.Element 来自列表的包含未导出的 Element.next Element.prev 指向下一个和上一个元素的指针在列表中。



    如果这些指针不会被设置为 nil ,当从列表中删除一个元素时,它们将持有对下一个和前一个元素包装的引用,包括与这些元素相关的值。



    请参阅此示例:

      var e2 * list.Element 

    func main(){
    listTest()
    fmt.Println(e2.Value)
    //此时我们希望列表中的所有内容都是
    // //随时收集的垃圾,我们只能参考e2。
    //如果e2.prev和e2.next不会设置为零,
    // e1和e3不能被释放!


    func listTest(){
    l:= list.New()
    e1:= l.PushBack(1)
    e2 = l。 PushBack(2)
    e3:= l.PushBack(3)
    //列表现在是[1,2,3]
    fmt.Println(e1.Value,e2.Value,e3 .Value)
    l.Remove(e2)
    //现在列表是[1,3],它不包含e2
    }

    listTest()中,我们用3个元素构建一个列表,并且将第2个元素存储在全局变量 e2 。然后我们删除这个元素。现在我们可以预料,除了 e2 (以及其中包含的值)之外,其他所有内容都会在 listTest()返回,因为列表不能在 listTest()函数之外访问。是的,我们有一个指向元素的 e2 ,但是 e2 has(应该有



    如果 prev 和<$ c $ e2 中的指针不会被设置为 nil ,由他们指出永远不会被释放,递归。但自 List.Remove() 在上面的例子中 e1 e3 - 随着包含在它们中的值 - 将被释放(在下一次垃圾收集运行时)。


    I'm learning Go, and as an exercise I wanted to implement a linked list. For reference I looked at the official Go code (https://golang.org/src/container/list/list.go) . One thing that stuck with me are these lines:

       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  } 
    

    I am curious as to how does setting pointers to nil in this case prevent memory leaks? If possible I would like to construct a program which has this flaw and see it while profiling with pprof (I would use a modified verion of the list.go without this nil pointer setting).


    For clarity of answer: If one of the nodes has an external pointer to it, then all of the adjacent removed nodes will have an active reference through that pointer and won't be removed.

    1. We create an external pointer pointing to Node2
    2. We remove nodes 2-4 from the list
    3. You would expect at this point only for the Node 1,2 & 5 to be alive and the rest to be GC-ed. However, due to Node2 still pointing to Node3 & etc., the entire chain remains uncollected.

    解决方案

    Your assumptions are correct. If there is a group of pointers pointing to each other, but there is no reference / pointer to any member of this group, the group will be detected as unreachable by the garbage collector and will be freed properly.

    But the explanation to memory leak is simple. We can get the list.Element wrappers from the list which contain the unexported Element.next and Element.prev pointers to the next and previous elements in the list.

    When removing an element from the list if these pointers would not be set to nil, they would held references to the next and previous element wrappers, including the values associated with those elements.

    See this example:

    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
    }
    

    In listTest() we build a list with 3 elements, and we store the 2nd element in a global variable e2. Then we remove this element. Now we would expect that except e2 (and the value wrapped in it) everything else gets garbage collected when listTest() returns, because the list is not accessible outside the listTest() function. Yes, we have a pointer in e2 to an element, but e2 has (should have) nothing to do with the list anymore as we removed it.

    If the prev and next pointers in e2 would not be set to nil, values wrapped in elements pointed by them could never be freed, recursively. But since List.Remove() properly sets those to nil, in the above example e1 and e3 –along with the values wrapped in them– will be freed (on next garbage collection run).

    这篇关于将指针设置为零以防止Golang中的内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆