golang之内存泄漏

1:什么是内存泄漏

内存泄漏(Memory Leak)并不是指物理上的内存消失,而是在写程序的过程中,由于程序的设计不合理导致对之前使用的内存失去控制,无法再利用这块内存区域,程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

如果发现程序内存泄漏了,解决起来也很简单退出程序,然后重启,不过对于对外提供服务的程序,肯定是不能接受的,那将导致服务中断,这是非常严重的事故。

2:垃圾回收GC

我们知道Golang垃圾回收 (GC garbage collection) 是一种自动内存管理机制,即我们在程序中定义一个变量后,会在内存中开辟相应空间进行存储。当不需要此变量后,需要手动销毁此对象,并释放内存, 而这种对不再使用的内存资源进行自动回收的功能即为垃圾回收,那么为什么还会出现内存泄漏呢?

因为过程中如果不注意,很容易造成内存泄漏的问题。比较常见的是发生在 slice、time.Ticker、goroutine 等的使用过程中,本文将从Golang内存泄漏的一些常见场景来看内存泄漏,然后学习如何避免和排查。

内存泄漏场景

1:slice

下面这段代码很多人会觉得没问题,我们知道slice底层有一个指向数组的指针地址,当两个slice 共享地址(同一个底层数组),其中一个为全局变量,另一个也无法被GC。推荐:煎鱼大佬对这个场景的分析

var a []int
func test(b []int) {
    a = b[:3]
    return
}

解决这个问题可以使用append的方法,append不会直接引用原来的数组,而是会新申请内存来存放数据。

2:time.After、 time.NewTicker

time.After通过函数的注释可以知道,每次time.After(duration x)会产生NewTimer(),在duration x到期之前,新创建的timer不会被GC,到期之后才会GC,NewTimer()返回一个Timer到只读channel中。

// 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.
func After(d Duration) <-chan Time {
	return NewTimer(d).C
}

func NewTimer(d Duration) *Timer {
	c := make(chan Time, 1)
	t := &Timer{
		C: c,
		r: runtimeTimer{
			when: when(d),
			f:    sendTime,
			arg:  c,
		},
	}
	startTimer(&t.r)
	return t
}

time.After 泄漏场景示例代码,我们来看看为什么会导致内存泄漏的发生,NewTimer(d).C 每次都是 return 了一个新的对象。并且我们是在 for 循环中定时执行 select,也就相当于每一次执行 select 我们都重新创建(实例化)了新的 time.After(),因此每一次执行 select time.After() 都会重新开始计时,只有到期之后新建的。

for true {
    select {
    case <-time.After(time.Minute * 5):
    // do something
  default:
        time.Sleep(time.Duration(1) * time.Second)
    }
}

time.After上述场景可以使用NewTimer()或者NewTicker()代替的方式主动释放资源。

//这两种情况下的idleDuration都可以
//idleDuration := time.NewTimer(time.Second * 5).C
idleDuration := time.After(time.Second * 5)
ticker := time.NewTicker(time.Second * 1)
for {
    select {
	case <-ticker.C:
		fmt.Println("xiaoxu")
	case <-idleDuration:
		fmt.Println("coding")
	return
	}
}
}()

time.NewTicker使用如下,是需要用defer Stop()去主动释放资源的,否则造成内存泄漏。

timer := time.NewTicker(time.Duration(2) * time.Second)
defer timer.Stop()  // 主动释放资源
for true {
    select {
         case <-timer.C:
         // do something
    default:
  time.Sleep(time.Duration(1) * time.Second)
 }
}

  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值