golang容易导致内存泄漏的6种情况汇总

作者:Yuan_sr 时间:2024-04-25 13:20:17 

1. 定时器使用不当

1.1 time.After()的使用

默认的time.After()是会有内存泄露问题的,因为每次time.After(duration x)会产生NewTimer(),在duration x到期之前,新创建的timer不会被GC,到期之后才会GC。

随着时间推移,尤其是duration x很大的话,会产生内存泄露的问题,应特别注意

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

为了保险起见,使用NewTimer()或者NewTicker()代替的方式主动释放资源,两者的区别请自行查阅或看我往期文章https://blog.csdn.net/weixin_38299404/article/details/119352884

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)
}
}

1.2 time.NewTicker资源未及时释放

在使用time.NewTicker时需要手动调用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)
}
}

2. select阻塞

使用select时如果有case没有覆盖完全的情况且没有default分支进行处理,最终会导致内存泄漏

2.1 导致goroutine阻塞的情况

func main() {
   ch1 := make(chan int)
   ch2 := make(chan int)
   ch3 := make(chan int)
   go Getdata("https://www.baidu.com",ch1)
   go Getdata("https://www.baidu.com",ch2)
   go Getdata("https://www.baidu.com",ch3)
   select{
       case v:=<- ch1:
           fmt.Println(v)
       case v:=<- ch2:
           fmt.Println(v)
   }
}

上述这种情况会阻塞在ch3的消费处导致内存泄漏

2.2 循环空转导致CPU暴涨

func main() {
fmt.Println("main start")
msgList := make(chan int, 100)
go func() {
for {
select {
case <-msgList:
default:

}
}
}()

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
s := <-c

fmt.Println("main exit.get signal:", s)
}

上述for循环条件一旦命中default则会出现循环空转的情况,并最终导致CPU暴涨

3. channel阻塞

channel阻塞主要分为写阻塞和读阻塞两种情况

空channel

func channelTest() {
 //声明未初始化的channel读写都会阻塞
   var c chan int
 //向channel中写数据
   go func() {
       c <- 1
       fmt.Println("g1 send succeed")
       time.Sleep(1 * time.Second)
   }()
 //从channel中读数据
   go func() {
       <-c
       fmt.Println("g2 receive succeed")
       time.Sleep(1 * time.Second)
   }()
   time.Sleep(10 * time.Second)
}

写阻塞

无缓冲channel的阻塞通常是写操作因为没有读而阻塞

func channelTest() {
   var c = make(chan int)
 //10个协程向channel中写数据
   for i := 0; i < 10; i++ {
       go func() {
           <- c
           fmt.Println("g1 receive succeed")
           time.Sleep(1 * time.Second)
       }()
   }
 //1个协程丛channel读数据
   go func() {
       c <- 1
       fmt.Println("g2 send succeed")
       time.Sleep(1 * time.Second)
   }()
 //会有写的9个协程阻塞得不到释放
   time.Sleep(10 * time.Second)
}

有缓冲的channel因为缓冲区满了,写操作阻塞

func channelTest() {
   var c = make(chan int, 8)
 //10个协程向channel中写数据
   for i := 0; i < 10; i++ {
       go func() {
           <- c
           fmt.Println("g1 receive succeed")
           time.Sleep(1 * time.Second)
       }()
   }
 //1个协程丛channel读数据
   go func() {
       c <- 1
       fmt.Println("g2 send succeed")
       time.Sleep(1 * time.Second)
   }()
 //会有写的几个协程阻塞写不进去
   time.Sleep(10 * time.Second)
}

读阻塞

期待从channel读数据,结果没有goroutine往进写数据

func channelTest() {
  var c = make(chan int)
 //1个协程向channel中写数据
 go func() {
   <- c
   fmt.Println("g1 receive succeed")
   time.Sleep(1 * time.Second)
 }()
 //10个协程丛channel读数据
 for i := 0; i < 10; i++ {
   go func() {
       c <- 1
       fmt.Println("g2 send succeed")
       time.Sleep(1 * time.Second)
   }()
 }
 //会有读的9个协程阻塞得不到释放
 time.Sleep(10 * time.Second)
}

4. goroutine导致的内存泄漏

4.1 申请过多的goroutine

例如在for循环中申请过多的goroutine来不及释放导致内存泄漏

4.2 goroutine阻塞

4.2.1 I/O问题

I/O连接未设置超时时间,导致goroutine一直在等待,代码会一直阻塞。

4.2.2 互斥锁未释放

goroutine无法获取到锁资源,导致goroutine阻塞

//协程拿到锁未释放,其他协程获取锁会阻塞
func mutexTest() {
   mutex := sync.Mutex{}
   for i := 0; i < 10; i++ {
       go func() {
           mutex.Lock()
           fmt.Printf("%d goroutine get mutex", i)
     //模拟实际开发中的操作耗时
           time.Sleep(100 * time.Millisecond)
       }()
   }
   time.Sleep(10 * time.Second)
}

4.2.3 死锁

当程序死锁时其他goroutine也会阻塞

func mutexTest() {
   m1, m2 := sync.Mutex{}, sync.RWMutex{}
 //g1得到锁1去获取锁2
   go func() {
       m1.Lock()
       fmt.Println("g1 get m1")
       time.Sleep(1 * time.Second)
       m2.Lock()
       fmt.Println("g1 get m2")
   }()
   //g2得到锁2去获取锁1
   go func() {
       m2.Lock()
       fmt.Println("g2 get m2")
       time.Sleep(1 * time.Second)
       m1.Lock()
       fmt.Println("g2 get m1")
   }()
 //其余协程获取锁都会失败
   go func() {
       m1.Lock()
       fmt.Println("g3 get m1")
   }()
   time.Sleep(10 * time.Second)
}

4.2.4 waitgroup使用不当

waitgroup的Add、Done和wait数量不匹配会导致wait一直在等待

5. slice 引起的内存泄漏

当两个slice 共享地址,其中一个为全局变量,另一个也无法被GC;

append slice 后一直使用,没有进行清理。

var a []int

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

6. 数组的值传递

由于数组时Golang的基本数据类型,每个数组占用不通的内存空间,生命周期互不干扰,很难出现内存泄漏的情况,但是数组作为形参传输时,遵循的时值拷贝,如果函数被多个goroutine调用且数组过大时,则会导致内存使用激增。

//统计nums中target出现的次数
func countTarget(nums [1000000]int, target int) int {
   num := 0
   for i := 0; i < len(nums) && nums[i] == target; i++ {
       num++
   }
   return num
}

因此对于大数组放在形参场景下通常使用切片或者指针进行传递,避免短时间的内存使用激增。

来源:https://blog.csdn.net/weixin_38299404/article/details/126805554

标签:golang,内存泄漏
0
投稿

猜你喜欢

  • 详解numpy1.19.4与python3.9版本冲突解决

    2021-10-26 22:51:26
  • python内置函数之eval函数详解

    2022-07-22 12:39:29
  • nginx简单配置多个php服务实例教程

    2023-06-11 22:53:30
  • 对python 中re.sub,replace(),strip()的区别详解

    2022-07-17 21:33:03
  • Python+Qt身体特征识别人数统计源码窗体程序(使用步骤)

    2021-06-03 10:40:54
  • Python中__slots__属性介绍与基本使用方法

    2023-11-22 02:45:59
  • python手写均值滤波

    2022-03-16 20:53:19
  • pyhanlp安装介绍和简单应用

    2022-04-10 14:22:13
  • Python编程编写完善的命令行工具

    2023-08-02 11:22:56
  • pyCaret效率倍增开源低代码的python机器学习工具

    2021-01-09 10:30:26
  • Flask框架之数据交互的实现

    2023-01-25 05:41:10
  • python多进程程序打包成exe的问题

    2023-08-07 13:39:03
  • Python+matplotlib+numpy实现在不同平面的二维条形图

    2023-11-11 21:01:58
  • 用CSS实现柱状图(Bar Graph)的方法(一)—基于列表元素的柱状图

    2008-05-26 13:03:00
  • Python qqbot 实现qq机器人的示例代码

    2021-05-18 12:43:43
  • 十个Python经典小游戏的代码合集

    2021-08-03 12:09:38
  • 利用pytorch实现对CIFAR-10数据集的分类

    2021-11-21 03:09:36
  • SQL Server连接查询的实用教程

    2024-01-28 10:41:30
  • Python 中 Pandas 文件操作和读取 CSV 参数详解

    2021-04-13 01:14:25
  • iframe高度自适应,兼容IE,FF

    2008-06-18 12:15:00
  • asp之家 网络编程 m.aspxhome.com