Need help to understand garbage collection in GoLa

2019-09-19 16:04发布

问题:

I'm a little bit confused with GoLang's garbage collector.

Consider this following code, where I implement reader interface for my type T.

type T struct {
   header Header
   data   []*MyDataType
}

func (t *T) Read(p []byte) (int, error) {
    t.Header = *(*Header) (t.readFileHeader(p))
    t.Data = *(*[]*MyDataType) (t.readFileData(p))
}

wherein the reader functions I will cast the data to MyDataType using the unsafe.Pointer which will point to slice created with the reflect module (this is more complicated, but for the sake of the example this should be enough)

func (t *T) readFileData(data []byte, idx int, ...) unsafe.Pointer {
    ...
    return unsafe.Pointer(&reflect.SliceHeader{Data : uintptr(unsafe.Pointer(&data[idx])), ...}) 
}

and If I am gonna read the data in different function

func (d *Dummy) foo() {
    data, _ := ioutil.ReadFile(filename)
    d.t.Read(data) <---will GC free data?
}

Now I'm confused if it is possible, that the GC will free loaded data from file after exiting the foo function. Or the data will be freed after the d.t is freed.

回答1:

To understand what GC might do to your variables, first you need to know how and where Go allocates them. Here is a good reading about escape analysis, that is how Go compiler decides where allocate memory, between stack or heap.

Long story short, GC will free memory only if it is not referenced by your Go program.

In your example, the reference to loaded data by data, _ := ioutil.ReadFile(filename) is passed to t.Data = *(*[]*MyDataType) (t.readFileData(p)) ultimately. Therefore, they will be referenced as long as (t *T) struct is referenced as well. As far as I can see from your code, the loaded data will be garbage-collected along with (t *T).



回答2:

According to the reflect docs, I've to keep a separate pointer to data *[]byte, to avoid garbage collection. So the solution is to add a referencePtr to

type T struct {
   header              Header
   data                   []*MyDataType
   referencePtr      *[]byte
}

which will point to my data inside the Read function

func (t *T) Read(p []byte) (int, error) {
    t.referencePtr = &p
    t.Header = *(*Header) (t.readFileHeader(p))
    t.Data = *(*[]*MyDataType) (t.readFileData(p))
}

or is this unnecessary?