У меня есть программа Go, которая работает непрерывно и полностью полагается на горутины + 1 поток manager
. Основной поток просто вызывает горутины и в противном случае спит.
Есть утечка памяти. Программа использует все больше и больше памяти, пока не истощит все 16 ГБ ОЗУ + 32 ГБ SWAP, и тогда каждая горутина паникует. На самом деле панику вызывает память ОС, обычно паника fork/exec ./anotherapp: cannot allocate memory
, когда я пытаюсь выполнить anotherapp
.
Когда это произойдет, все рабочие потоки перестанут работать, будут восстановлены и перезапущены. Таким образом, каждая горутина будет паниковать, восстанавливаться и перезапускаться... и в этот момент использование памяти не уменьшится, оно останется на уровне 48 ГБ, хотя сейчас практически ничего не выделено. Это означает, что все горутины всегда будут паниковать, поскольку памяти никогда не бывает достаточно, пока весь исполняемый файл не будет уничтожен и полностью перезапущен.
Всего это около 50 000 строк, но фактическая проблемная область выглядит следующим образом:
type queue struct {
identifier string
type bool
}
func main() {
// Set number of gorountines that can be run
var xthreads int32 = 10
var usedthreads int32
runtime.GOMAXPROCS(14)
ready := make(chan *queue, 5)
// Start the manager goroutine, which prepared identifiers in the background ready for processing, always with 5 waiting to go
go manager(ready)
// Start creating goroutines to process as they are ready
for obj := range ready { // loops through "ready" channel and waits when there is nothing
// This section uses atomic instead of a blocking channel in an earlier attempt to stop the memory leak, but it didn't work
for atomic.LoadInt32(&usedthreads) >= xthreads {
time.Sleep(time.Second)
}
debug.FreeOSMemory() // Try to clean up the memory, also did not stop the leak
atomic.AddInt32(&usedthreads, 1) // Mark goroutine as started
// Unleak obj, probably unnecessary, but just to be safe
copy := new(queue)
copy.identifier = unleak.String(obj.identifier) // unleak is a 3rd party package that makes a copy of the string
copy.type = obj.type
go runit(copy, &usedthreads) // Start the processing thread
}
fmt.Println(`END`) // This should never happen as the channels are never closed
}
func manager(ready chan *queue) {
// This thread communicates with another server and fills the "ready" channel
}
// This is the goroutine
func runit(obj *queue, threadcount *int32) {
defer func() {
if r := recover(); r != nil {
// Panicked
erstring := fmt.Sprint(r)
reportFatal(obj.identifier, erstring)
} else {
// Completed successfully
reportDone(obj.identifier)
}
atomic.AddInt32(threadcount, -1) // Mark goroutine as finished
}()
do(obj) // This function does the actual processing
}
Насколько я вижу, когда функция do
(последняя строка) завершается либо по завершению, либо по панике, функция runit
затем завершается, что полностью завершает горутину, что означает, что вся память из этой горутины теперь должна быть свободна. . Вот что происходит сейчас. Что происходит, так это то, что это приложение просто использует все больше и больше и больше памяти, пока оно не перестанет функционировать, все runit
горутины паникуют, но память не уменьшается.
Профилирование ничего подозрительного не выявляет. Утечка, похоже, выходит за рамки профилировщика.
manager()
, которая просто бесконечно генерирует ввод, и функциюdo()
, которая ничего не делает (пустая функция). Посмотрите, есть ли у вас все еще утечка памяти. Если нет, то, очевидно, утечка находится в коде, который вы не опубликовали, и в этом случае мы ничего не можем сделать в текущем состоянии вопроса. - person icza   schedule 04.02.2015type
, которое не будет компилироваться. Можете ли вы показать реальный код? - person Cerise Limón   schedule 04.02.2015goroutineCount, _ := runtime.GoroutineProfile(nil)
может быстро сказать вам, сколько работает. - person twotwotwo   schedule 05.02.2015