我有一个用Go在Windows上运行的数据转发应用程序。 它有一个使用 gotk3/gtk3 的 GUI 前端。 在正常情况下,Windows 报告该程序使用大约 25MB RAM。
GUI 显示每 2 秒更新一次的通道统计信息,我仅在 GUI 具有顶级焦点时更新这些统计信息。 我可能会尝试增加更新间隔作为开始。
当更新运行时,内存使用缓慢但不可避免地增加,直到程序在一个小时左右崩溃,但这似乎是 Go memstats 之外的内存。 memstats 报告的数字变化不大,所以肯定是 gtk3 使用了内存,而 Windows 没有收回。
一旦程序失去焦点并且统计数据更新停止,内存使用量就会停止增长,但 Windows 在另外 7 或 8 分钟内不会恢复内存使用量,此时内存使用量开始稳定下降到应有的水平。
我不知道如何停止内存蠕变,我的猜测是 gtk3 的 Gotk3 包装释放内存的速度并没有像它用完内存的那么快。
我的问题是我可以监控 Windows 在 Go 中分配给程序的内容,然后我可以暂停统计数据更新,直到内存使用量再次下降吗?
我找不到可以提供该信息的包,因为我说 Go 运行时都没有。Memstats 似乎与 Windows 所说的相符。
添加代码是因为 a) 每个人都喜欢一些代码,b) 有人可能知道如何阻止 gotk3 耗尽内存。
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
}
通过
syscall
或 golang.org/x/sys
使用 Windows API
您可以使用
syscall
或 golang.org/x/sys/windows
包与 Windows API 进行交互。具体来说,GlobalMemoryStatusEx
或 GetProcessMemoryInfo
API 可以为您提供系统级内存使用统计信息,包括私有字节和工作集大小(这是任务管理器通常报告的内容)。
我使用
GetProcessMemoryInfo
准备了一个示例:
go get golang.org/x/sys/windows
GetProcessMemoryInfo
API 获取内存使用情况。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() // Get current process handle
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
}
这应该可以让您深入了解 Windows 报告的 Go 应用程序的内存使用情况,而不仅仅是 Go 堆。
注意:现在您可以使用它来根据内存使用情况监控和管理应用程序暂停或播放状态。
现在,如果即使停止更新后内存使用量仍然继续上升,则可能是gotk3管理内存的方式存在内存泄漏。
在这种情况下,我建议您在不再需要时手动释放表面、图像或列表等资源。 另请记住,GTK 对象需要适当的清理,而 Go 的垃圾收集器不会自动清理在 Go 外部分配的资源(如 GTK)。