0
votes

I have encountered strange (as for a man who is new to golang) behaviour of my goroutine:

go func(...) {
  for {
    buffer := make([]byte, 1024)
     ...
  } 
}

It slowly eats RAM. I understand that it is caused by calling make in an endless loop; it just allocates new memory every time. But i don't quite get why this construction:

  var buffer []byte
  for {
    buffer = make([]byte, 1024)
     ...
    }
  } 

..works well, while the first one doesn't. Shouldn't garbage collector detect that memory which old buf was pointing at is unreachable in both cases? And maybe there are some other similar traps that a go-newbie should know?

Also, if i will return this goroutine, will leaked memory be freed?

UPD: full memory leaking routine code:

go func(dataChannel chan []byte, errorChannel chan error) {
  for {
    buf := make([]byte, 1024)
    _, err := conn.Read(buf) 
    if err != nil {
      errorChannel<- err
      break
    }
    dataChannel<- buf
  } 
} (dataChannel, errorChannel)
2
Never use a busy loop, it's always a mistake. The first just happens to break things worse than the second, because the first could be doing something else. Yes, the memory can eventually be freed if you get that far. - JimB
Сould you please be more specific on dangers that such busy loop can possibly hide? This routine reads from socket and sends recieved data through channel. AFAIK, net.Read() blocks the execution and consumes no excess CPU time, and there is a loop break in case that TCP connection will broke. I can make it one-timed, and re-run it after getting message, but i don't see yet how it could be dangerous. - navij
"And maybe there are some other similar traps that a go-newbie should know?" These are not traps, neither to newbies nor to veterans. These are handcrafted pathological code snippets nobody would ever write. - Volker
@navij: Then your example isn't complete enough. If you are doing something besides calling make in the loop then your problem is likely elsewhere. Please show a complete example. If you want to see how memory is being collected, set GODEBUG=gctrace=1 - JimB
@navij: that first example is exactly what we're talking about, it's a useless loop that is spinning as fast as possible, it breaks your program, but no sane program should ever have that. That is not equivalent to a loop like in your question that calls Read on a net.Conn. - JimB

2 Answers

0
votes
  for {
        var mem runtime.MemStats
        runtime.ReadMemStats(&mem)
        fmt.Printf("alloc [%v] \t heapAlloc [%v] \n", mem.Alloc, mem.HeapAlloc)
        time.Sleep(2000 * time.Millisecond)
    }
  • added the above lines in your main function for loop (in the leaking code link given above)
  • new code : https://pastebin.com/UQbYuVN7
  • while running this shows that memory is being re-collected at regular intervals by gc
-2
votes

Thanks to JimB and his advice to set enviroment var GODEBUG=gctrace=1, i discovered that i had no env var GOGC set in my system, because of which garbage collector wasn't functioning at all. Setting it to default value was everything that i needed.