用大片字符串理解Golang内存管理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用大片字符串理解Golang内存管理相关的知识,希望对你有一定的参考价值。
我正在为Go编写的Twitch.tv网站上的聊天机器人。
机器人的一个特征是奖励用户观看特定流的积分系统。此数据存储在SQLite3数据库中。
为了获得观众,机器人进行API调用以抽搐并收集流的所有当前观众。然后将这些观众放入一串字符串中。
总观看者的范围可以从几个到20,000或更多。
机器人做了什么
- 进行API调用
- 将所有观看者存储在一个字符串中
- 对于每个观看者,bot会相应地迭代并添加点。
- Bot在下一次迭代之前清除此切片
码
type Viewers struct {
Chatters struct {
CurrentModerators []string `json:"moderators"`
CurrentViewers []string `json:"viewers"`
} `json:"chatters"`
}
func RunPoints(timer time.Duration, modifier int, conn net.Conn, channel string) {
database := InitializeDB() // Loads database through SQLite3 driver
var Points int
var allUsers []string
for range time.NewTicker(timer * time.Second).C {
currentUsers := GetViewers(conn, channel)
tx, err := database.Begin()
if err != nil {
fmt.Println("Error starting points transaction: ", err)
}
allUsers = append(allUsers, currentUsers.Chatters.CurrentViewers...)
allUsers = append(allUsers, currentUsers.Chatters.CurrentModerators...)
for _, v := range allUsers {
userCheck := UserInDB(database, v)
if userCheck == false {
statement, _ := tx.Prepare("INSERT INTO points (Username, Points) VALUES (?, ?)")
statement.Exec(v, 1)
} else {
err = tx.QueryRow("Select Points FROM points WHERE Username = ?", v).Scan(&Points)
if err != nil {
} else {
Points = Points + modifier
statement, _ := tx.Prepare("UPDATE points SET Points = ? WHERE username = ?")
statement.Exec(Points, v)
}
}
}
tx.Commit()
allUsers = allUsers[:0]
currentUsers = Viewers{} // Clear Viewer object struct
}
预期的行为
当吸引成千上万的观众时,我自然会期望系统资源变得非常高。这可以使用3.0 MB的RAM将机器人转为20 MB +。当然,成千上万的元素占用了大量的空间!
然而,还有其他事情发生。
实际行为
每次调用API时,RAM都会按预期增加。但是因为我清除了切片,我预计它会回落到它的“正常”3.0 MB的使用量。
但是,每次API调用的RAM使用量会增加,即使流的查看者总数有所增加,也不会降低。
因此,给了几个小时,机器人将很容易消耗100 + MB的ram,这对我来说似乎不对。
我在这里错过了什么?我对编程和CS一般都是新手,所以也许我正在尝试修复一些不是问题的东西。但这几乎听起来像是一个内存泄漏给我。
我已经尝试通过Golang的运行时库强制垃圾收集和释放内存,但这并没有解决它。
要了解这里发生了什么,您需要了解切片的内部结构以及发生了什么。你应该从https://blog.golang.org/go-slices-usage-and-internals开始
简单回答:切片提供了对底层数组的一部分的视图,当您尝试截断切片时,您所做的只是减少对数组的视图,但底层数组不受影响并且仍占用同样多的内存。实际上,通过继续使用相同的数组,您永远不会减少您正在使用的内存量。
我鼓励你阅读它是如何工作的,但作为为什么没有释放实际内存的例子,看看这个简单程序的输出,该程序演示了如何更改切片不会截断分配的内存引擎盖下:https://play.golang.org/p/PLEZba8uD-L
重新切片时:
allUsers = allUsers[:0]
所有元素仍在后备阵列中,无法收集。内存仍然被分配,这将在下一次运行中节省一些时间(它不必大量调整数组大小,节省了缓慢的分配),这是将其重新调整为零长度而不是仅仅转储它的点。
如果您希望将内存释放到GC,则需要完全转储它并每次创建一个新的切片。这会更慢,但在运行之间使用更少的内存。但是,这并不一定意味着您将看到该进程使用的内存更少。 GC收集未使用的堆对象,然后可能最终将该内存释放到操作系统,如果其他进程正在施加内存压力,操作系统最终可能会回收它。
以上是关于用大片字符串理解Golang内存管理的主要内容,如果未能解决你的问题,请参考以下文章