I have a data forwarding application running on Windows in Go. This has a gui front end using gotk3/gtk3. Under normal conditions Windows reports the program using about 25MB RAM.
The gui displays channel statistics that update every 2 seconds, I only update these stats when the gui has top-level focus. I might try increasing the update interval for a start.
When the updates are running, memory use slowly but inexorably builds up, until the program crashes after an hour or so, however this seems to be memory outside the Go memstats. Numbers reported by memstats don't change much, so it must be memory that gtk3 uses and Windows doesn't get back.
Once the program loses the focus and the stats update stops, the memory use stops growing but Windows doesn't get it back for another 7 or 8 minutes, when memory usage starts dropping steadily back to where it should be.
I have no idea how to stop the memory creep, my guess is that the gotk3 wrapping for gtk3 isn't releasing memory as fast as it can use it up.
My question is can I monitor what Windows has allocated to the program within Go, then I could suspend stats updates until memory use falls again?
I can't find a package that will give me that information, as I say none of the Go runtime.Memstats seem to match what Windows says.
Code added because a) everyone likes a bit of code and b) someone might know how to stop gotk3 using up memory.
func updateStats() {
MainListStore.ForEach(func(tmodel *gtk.TreeModel, path *gtk.TreePath, iterfe *gtk.TreeIter) bool {
// get the channel no
value, _ := tmodel.GetValue(iterfe, MCOL_INPORT)
goValue, _ := value.GoValue()
key := goValue.(int)
// copy stats to liststore
Mutex.Lock()
err := MainListStore.Set(iterfe,
[]int{MCOL_STATPIX, MCOL_STATINT, MCOL_SPDIN, MCOL_SPDOUT},
[]interface{}{Pixes[Statmap[key][0]], Statmap[key][0], Statmap[key][1], Statmap[key][2]})
Mutex.Unlock()
if err != nil {
Logit.Printf("Error: error updating stats chan %d, %v", key, err)
}
return false // keep iterating
})
return
}
Use Windows API via syscall
or golang.org/x/sys
You can use the syscall
or golang.org/x/sys/windows
packages to interact with Windows APIs. Specifically, the GlobalMemoryStatusEx
or GetProcessMemoryInfo
APIs can give you system-level memory usage stats, including private bytes and working set size (This is what Task Manager typically reports).
I prepared one example using GetProcessMemoryInfo
:
go get golang.org/x/sys/windows
GetProcessMemoryInfo
API to get the memory usage.package main
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var (
psapi = windows.NewLazySystemDLL("psapi.dll")
procGetProcessMemoryInfo = psapi.NewProc("GetProcessMemoryInfo")
)
type PROCESS_MEMORY_COUNTERS struct {
cb uint32
PageFaultCount uint32
PeakWorkingSetSize uintptr
WorkingSetSize uintptr
QuotaPeakPagedPoolUsage uintptr
QuotaPagedPoolUsage uintptr
QuotaPeakNonPagedPoolUsage uintptr
QuotaNonPagedPoolUsage uintptr
PagefileUsage uintptr
PeakPagefileUsage uintptr
}
func GetProcessMemoryInfo(handle windows.Handle) (PROCESS_MEMORY_COUNTERS, error) {
var memCounters PROCESS_MEMORY_COUNTERS
memCounters.cb = uint32(unsafe.Sizeof(memCounters))
r, _, err := procGetProcessMemoryInfo.Call(
uintptr(handle),
uintptr(unsafe.Pointer(&memCounters)),
uintptr(memCounters.cb),
)
if r == 0 {
return memCounters, err
}
return memCounters, nil
}
func main() {
// pid:= windows.GetCurrentProcess() previously
pid := windows.CurrentProcess() // Get current process handle (new method name)
memInfo, err := GetProcessMemoryInfo(pid)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Memory Usage:\n")
fmt.Printf("Working Set Size: %v bytes\n", memInfo.WorkingSetSize) // actual physical memory used by application
fmt.Printf("Pagefile Usage: %v bytes\n", memInfo.PagefileUsage) // virtual memory
}
This should give you insight into what Windows reports as the memory usage of your Go application, beyond just the Go heap.
NOTE: Now you can use this to monitor and manage the application pause or play state based on memory usage.
Now, If the memory usage still continues to rise, even after stopping updates, it's possible there is a memory leak in the way gotk3 is managing memory.
In that case i would suggest you manually free resources like surfaces, images, or lists, when no longer needed. Also keep in mind that GTK objects need proper cleanup, and Go's garbage collector won't automatically clean up resources allocated outside of Go (like GTK).