[编程技术] Golang time.After内存泄漏分析
作者:CC下载站 日期:2022-05-06 00:00:51 浏览:18 分类:编程开发
背景
我刚转做go语言开发开始写入职小程序的时候,写下了如下的代码:
1
2
3
4
5
6
7
8
9
10
for {
select {
case conn := <- conns:
... //do someting
return conn
case <- time.After(c.Timeouot):
... //do something
return xxx
}
}
这只是一个简单的从连接池取连接
的代码,如果连接池没有可用连接,就在超时后创建一个新的连接使用。
说实话,我觉得我的代码可优雅了,后来对go语言有了更深入的了解之后,发现我写的代码有着明显的内存泄漏的问题。
熟悉go语言的朋友可能已经看出来了,内存泄漏的代码就是time.After
。
这是go语言经典的坑
,或者说是对go语言机制不了解的初学者经常会踩的坑,稍不留神就会掉到坑里去。
今天我们就来分析一下这个time.After
内存泄漏之谜。
我们来用最简单的例子模拟一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main
import (
"fmt"
"time"
)
//define a channel
var chs chan int
func Get() {
for {
select {
case v := <- chs:
fmt.Printf("print:%v\n", v)
case <- time.After(3 * time.Minute):
fmt.Printf("time.After:%v", time.Now().Unix())
}
}
}
func Put() {
var i = 0
for {
i++
chs <- i
}
}
func main() {
chs = make(chan int, 100)
go Put()
Get()
}
咱也别管代码优不优雅,就很简单的逻辑,先往channel里面存数据,然后不停的使用for select case
语法从channel里面取数据。
我相信大家一眼就能看懂。就是这么简单的代码却会导致内存泄漏。
发现我们的进程MEM
这一列的值迅速增加,不到一分钟就占用了6G的内存,明显产生了内存泄漏。
原理分析
要了解为什么会产生内存泄漏,我们需要看看time package
里面time.After
函数的的定义。https://pkg.go.dev/time
func After(d Duration) <-chan Time
After waits for the duration to elapse and then sends the current time on the returned channel. It is equivalent to NewTimer(d).C. The underlying Timer is not recovered by the garbage collector until the timer fires. If efficiency is a concern, use NewTimer instead and call Timer.Stop if the timer is no longer needed.
该方法可以在一定时间(根据所传入的 Duration)后主动返回 time.Time 类型的 channel 消息。
注意: 描述里面写的很清楚:
在计时器触发之前,垃圾收集器不会回收Timer
如果考虑效率,需要使用NewTimer替代
1
2
3
4
5
6
7
8
for {
select {
case v := <- chs:
fmt.Printf("print:%v\n", v)
case <- time.After(3 * time.Minute):
fmt.Printf("time.After:%v", time.Now().Unix())
}
}
回过头来看我们的代码,这里我们的定时时间设置的是3分钟, 在for循环内每次select的时候,都会实例化一个一个新的定时器。该定时器在3分钟后,才会被激活,但是激活后已经跟select无引用关系,被gc给清理掉。
重点是: 在select里面虽然我们没有执行到time.After,但是这个对象已经初始化了,依然在时间堆里面,定时任务未到期之前,是不会被gc清理的。
解决方案
根据time.After
的解释,我们知道,for select case
里面最好不要用time.After
,go语言对这个方法的设计决定了它不能这么用,否则会有内存泄漏的风险。
至于解决方案,time package
的文档也说明了,使用NewTimer
来做定时器,不需要每次都创建定时器对象。
time.After
虽然调用的是timer定时器,但是他没有使用time.Reset()
方法再次激活定时器,所以每一次都是新创建的实例,才会造成的内存泄漏。
而我们使用NewTimer
创建定时器,再加上time.Reset
每次重新激活定时器,即可完美解决问题。
1
func NewTimer(d Duration) *Timer
NewTimer creates a new Timer that will send the current time on its channel after at least duration d.
而Timer
的指针里面封装了:
1
2
3
type Timer struct {
C <-chan Time
}
The Timer type represents a single event. When the Timer expires, the current time will be sent on C, unless the Timer was created by AfterFunc. A Timer must be created with NewTimer or AfterFunc.
我们改造一下上面的Get函数,使用time.NewTimer
来初始化Timer
的指针,并用Reset
来重置定时器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func Get() {
delay := time.NewTimer(3 * time.Minute)
defer delay.Stop()
for {
delay.Reset(3 * time.Minute)
select {
case v := <- chs:
fmt.Printf("print:%v\n", v)
case <- delay.C:
fmt.Printf("time.After:%v", time.Now().Unix())
}
}
}
记得一定要使用Reset重置定时器,如果不重置,那么定时器还是从创建的时候开始计算时间流逝。使用了Reset之后,每次都从当前Reset的时间开始算。
这么改之后,再次测试代码,发现内存一直稳定。
总结
在for select case
里面使用定时器一定要小心,定时器只有等到计时器触发之后才会被垃圾回收器回收,而time.After
是单次触发且无法Reset。
这种需要循环内触发定时器的用例,还是要使用time.NewTimer
并手动Reset
。
<全文完>
猜你还喜欢
- 03-29 [编程相关] Winform窗体圆角以及描边完美解决方案
- 03-29 [前端问题] has been blocked by CORS policy跨域问题解决
- 03-29 [编程相关] GitHub Actions 入门教程
- 03-29 [编程探讨] CSS Grid 网格布局教程
- 10-12 [编程相关] python实现文件夹所有文件编码从GBK转为UTF8
- 10-11 [编程算法] opencv之霍夫变换:圆
- 10-11 [编程算法] OpenCV Camshift算法+目标跟踪源码
- 10-11 [Python] python 创建 Telnet 客户端
- 10-11 [编程相关] Python 基于 Yolov8 + CPU 实现物体检测
- 03-15 [脚本工具] 使用go语言开发自动化脚本 - 一键定场、抢购、预约、捡漏
- 01-08 [编程技术] 秒杀面试官系列 - Redis zset底层是怎么实现的
- 01-05 [编程技术] 《Redis设计与实现》pdf
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[电影] 《环太平洋两部合集》 4K REMUX原盘 [杜比视界] 国英双语音轨 [内封特效字幕] [133.8G]
[电影] 异人之下 The Traveller 2024✨【影版】【4K正式版/HQ超高码/DDP5.1】✚【1080高码】无水印/无压缩
[动漫] 头文字D 动漫 (1998) S01-S06季 1080P 国粤日音轨 续作 剧场版 电影
[小说] 知轩藏书全站7667册txt小说合集精心校对版
[杂志] 电脑爱好者杂志14年 超全 [PDF]
[电影] 西游记全部版本-4K高清修复-总计384G-1986+1996+1998+2002+2010浙版+西游记后传
[纪录片] 【国家地理百年纪念典藏】超经典100集全 MP4格式 (绝佳学习资料)27GB
[纪录片] B站食贫道收费纪录片 *迷失东京* [1080P] 揭露日本大家感兴趣却不为人知的秘密
[网络线报] 城通网盘福利线报解析器 - 获取直连下载地址
[福利线报] 一个「脚本」搞定六大网盘(百度/阿里/天翼/迅雷/夸克/移动)
[游戏] 《黑神话悟空》免安装学习版【全dlc整合完整版】+Steam游戏解锁+游戏修改工具!
[动画] 《名侦探柯南》名侦探柯南百万美元的五菱星 [TC] [MP4]
[电视剧集] [BT下载][黑暗城市- 清扫魔 Dark City: The Cleaner 第一季][全06集][英语无字][MKV][720P/1080P][WEB-RAW]
[涨点姿势] 男性性技宝典:14招实战驭女术——爱抚、按摩、催情、姿势、高潮全攻略
[动画] 2002《火影忍者》720集全【4K典藏版】+11部剧场版+OVA+漫画 内嵌简日字幕
[剧集] 《斯巴达克斯》1-4季合集 无删减版 1080P 内嵌简英特效字幕
[CG剧情] 《黑神话:悟空》158分钟CG完整剧情合集 4K120帧最高画质
[短剧] 被下架·禁播的羞羞短剧·午夜短剧合集
[游戏] 黑神话悟空离线完整版+修改器
[图像处理] 光影魔术手v4.6.0.578绿色版
[影视] 美国内战 4K蓝光原盘下载+高清MKV版/内战/帝国浩劫:美国内战(台)/美帝崩裂(港) 2024 Civil War 63.86G
[影视] 一命 3D 蓝光高清MKV版/切腹 / 切腹:武士之死 / Hara-Kiri: Death of a Samurai / Ichimei 2011 一命 13.6G
[影视] 爱情我你他 蓝光原盘下载+高清MKV版/你、我、他她他 2005 Me and You and Everyone We Know 23.2G
[影视] 穿越美国 蓝光原盘下载+高清MKV版/窈窕老爸 / 寻找他妈…的故事 2005 Transamerica 20.8G
[电影] 《黄飞鸿》全系列合集
[Android] 开罗游戏 ▎像素风格的模拟经营的游戏厂商安卓游戏大合集
[游戏合集] 要战便战 v0.9.107 免安装绿色中文版
[资源] 精整2023年知识星球付费文合集136篇【PDF格式】
[系统]【黑果小兵】macOS Big Sur 11.0.1 20B50 正式版 with Clover 5126 黑苹果系统镜像下载
[美图] 【经典收藏美图集合】1500多张韩国美女高清图片让你的收藏夹更加丰富多彩
- 最新评论
-
有靳东!嘻嘻奥古斯都.凯撒 评论于:10-28 流星花园是F4处女作也是4人集体搭配的唯一一部!奥古斯都.凯撒 评论于:10-28 找了好久的资源,终于在这里找到了。感谢本站的资源和分享。谢谢AAAAA 评论于:10-26 找了好久的资源,终于在这里找到了。感谢本站的资源和分享。谢谢password63 评论于:10-26 找了好久的资源,终于在这里找齐了!!!!blog001 评论于:10-21 找了好久的资源,终于在这里找齐了!!!!blog001 评论于:10-21 找了好久的资源,终于在这里找到了。感谢本站的资源和分享。谢谢WillKwok 评论于:10-09 感谢分享1234123 评论于:10-07 太好了终于找到了谢谢Tom 评论于:10-07 谢谢分享loonghd 评论于:09-30
- 热门tag