0

I am very new to Golang, but have lots of Java and C experience.

My application has a fairly complex,hierarchical object model built from a database, ORM-style, which is intended to be long-lived. The structures are built up using pointers created with new and/or make.

There are situations when I need to flush the current objects from memory and rebuild them from the DB. Is it possible to encourage Golang to reclaim the memory by traversing the structures from the lower levels, setting references to nil?

Is there a better way to do this?

(I am starting to realize that this architecture is not really typical for Go, which prefers to keep things on the stack. However, I am not free to discard it and start over, or to change to a different programming language.)

Thanks in advance for any guidance.

  • 1
    There is no differentiation between stack and heap in the language, that's purely an implementation detail of the runtime which you are using. The standard Go runtime garbage collector is precise and does not use reference counting, so either an object is globally reachable or it is not. Circular references make no difference in reachability, only in how the pointers are traversed. – JimB May 04 '22 at 14:50

1 Answers1

3

The point of garbage collected languages is that you don't have to worry about memory management, you don't have to worry about memory allocations and deallocations.

The general principle is: as long as you have a "reference" to some object, it will not be garbage collected; and objects that no one has a reference of, will be garbage collected.

This principle is of course "recursive". If you have an A object holding a pointer to a B object, and there's only a single reference to A (and no one else has a reference to B), once that reference "goes away", both A and B will be garbage collected.

So if you want some complex object hierarchy to be freed, just make sure you don't keep pointers to it, and it will be freed automatically. You do not have to traverse it recursively to zero pointers.

There is one important thing to keep in mind though: If you have a pointer to some "part" of an object, that will keep the whole object in memory. For example if you create a struct, and take the address of one of its fields and store it somewhere, that pointer will keep the whole struct in memory. Similarly, if you create a slice and you take the address of one of its elements (and store it), that will keep the whole slice (or more precisely its backing array) in memory. This also applies if you reslice the slice to "hide" elements, e.g.:

s := make([]*int, 10)
s[8] = new(int)
s = s[:2]

Here we created a slice of 10 int pointers, we assigned a pointer to the index 8, then resliced the slice to only hold the first 2 elements. There is a backing array in the background with storage for 10 pointers, which is not cleared by the above slicing operation, so the pointed int (at index 8 in the original slice) will remain in memory (as long as you have a reference to the slice or its backing array). For details, see Does go garbage collect parts of slices?

See related questions:

Freeing unused memory?

Cannot free memory once occupied by bytes.Buffer

icza
  • 389,944
  • 63
  • 907
  • 827