Karel Bílek
Karel Bílek

Reputation: 37694

Does go GC keep whole objects in memory, while interior pointer to one field remains? Can this cause memory leaks?

Does go's GC keep whole objects in memory, while interior pointer to a field remains?

Can this cause memory leaks, in a code like this? Or is go's GC "smart enough" to notice that the rest of the object is no longer needed and clear it from memory?

package main

import (
    "fmt"
)

const aLot = 500000000

func getInteriorPointer() *int {
    type bigStruct struct {
        someBigThing [aLot]int
        smallThing   int
    }
    b := bigStruct{smallThing: 3}
    return &b.smallThing
}

func main() {
    p := getInteriorPointer()
    // keep using p in the rest of the app, 
    // never using someBigThing from the struct
    fmt.Println(*p)
}

Upvotes: 1

Views: 516

Answers (2)

peterSO
peterSO

Reputation: 166764

Yes. Memory will be kept until there is no longer any reference to any part of the allocation.

The usual example is given in slice tricks

SliceTricks

Delete without preserving order

a[i] = a[len(a)-1] 
a = a[:len(a)-1]

NOTE If the type of the element is a pointer or a struct with pointer fields, which need to be garbage collected, the above implementations of Cut and Delete have a potential memory leak problem: some elements with values are still referenced by slice a and thus can not be collected.

Upvotes: 3

Karel Bílek
Karel Bílek

Reputation: 37694

Looking at profiler, it seems to indeed be a memory leak (go1.11.5 darwin/amd64)


Let's look at profiler.

I found an online example for debugging memory. By adapting to my example, I did this:

package main

import (
    "fmt"
    "os"
    "runtime"
    "runtime/debug"
    "runtime/pprof"
)

const aLot = 5000000

func getInteriorPointer() *int {
    type bigStruct struct {
        someBigThing [aLot]int
        smallThing   int
    }
    b := bigStruct{smallThing: 3}
    return &b.smallThing
}

func main() {
    p := getInteriorPointer()
    runtime.GC()
    debug.FreeOSMemory()
    fmem, _ := os.Create("prof.prof")
    pprof.WriteHeapProfile(fmem)

    // keep using p in the rest of the app,
    // never using someBigThing from the struct
    fmt.Println(*p)
}

Now I run go tool pprof prof.prof and list main

ROUTINE ======================== main.getInteriorPointer in /Users/karel/exp/exp.go
   38.15MB    38.15MB (flat, cum)   100% of Total
         .          .     13:func getInteriorPointer() *int {
         .          .     14:   type bigStruct struct {
         .          .     15:           someBigThing [aLot]int
         .          .     16:           smallThing   int
         .          .     17:   }
   38.15MB    38.15MB     18:   b := bigStruct{smallThing: 3}
         .          .     19:   return &b.smallThing
         .          .     20:}
         .          .     21:
         .          .     22:func main() {
         .          .     23:   p := getInteriorPointer()
ROUTINE ======================== main.main in /Users/karel/exp/exp.go
         0    38.15MB (flat, cum)   100% of Total
         .          .     18:   b := bigStruct{smallThing: 3}
         .          .     19:   return &b.smallThing
         .          .     20:}
         .          .     21:
         .          .     22:func main() {
         .    38.15MB     23:   p := getInteriorPointer()
         .          .     24:   runtime.GC()
         .          .     25:   debug.FreeOSMemory()
         .          .     26:   fmem, _ := os.Create("prof.prof")
         .          .     27:   pprof.WriteHeapProfile(fmem)
         .          .     28:

It seems garbage collector indeed doesn't remove the unnecessary object from memory. If I understand the format correctly. (I might not, since there is almost no documentation.)

Upvotes: 0

Related Questions