Setting pointers to nil to prevent memory leak in Golang

前端 未结 2 1491
南笙
南笙 2020-12-08 22:13

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) . On

相关标签:
2条回答
  • 2020-12-08 22:22

    Golang garbage collector is based on the tri-color mark-and-sweep algorithm. In short, every memory you're program use is associated to a color. The color determine if the memory shall be garbaged or not.

    This algorithm will flag a memory to be freed if this memory is not referenced somewhere (directly and indirectly). But if we take a look at the code:

    e.prev.next = e.next
    e.next.prev = e.prev
    

    This copy the pointer in e.next into e.prev.next. Now, let's say you want to update e.prev.next by a new fully created element.

    The previously removed element won't be garbaged because it is still referenced by e.next.

    This is why those lines exist:

    e.next = nil // avoid memory leaks
    e.prev = nil // avoid memory leaks
    

    This prevent from leaving old references, and thus prevent from memory leaks.

    0 讨论(0)
  • 2020-12-08 22:34

    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).

    0 讨论(0)
提交回复
热议问题