Golang处理内存溢出

文章介绍了在Golang中处理内存溢出问题的方法,包括使用pprof进行内存和CPU分析,通过GoMemstats监控内存信息,以及在程序崩溃时生成dump文件进行后期分析。此外,还提到了优化内存使用和理解垃圾回收机制的重要性。
摘要由CSDN通过智能技术生成

背景:

最近系统在压测过程中发现主程序在并发增大后会出现主程序闪退现象,几经波折,认为有可能是内存溢出引起的
正好对 Golang 里分析 dump 这块还没怎么涉及,借此契机研究一下。

前言:

查看社区后,发现在Golang中,发生内存溢出通常会导致程序直接崩溃退出,而无法像其他语言一样生成core dump文件。因此,Golang社区提供了一些工具可以帮助用户进行内存分析。以下是两个常用的工具:

1. pprof

pprof是Golang内置的性能分析工具,在分析内存使用方面非常有用。通过设置一个标志位并在程序退出时打开pprof服务器,程序可以向pprof服务器报告一些性能数据,例如内存使用情况。可以在代码中加入以下代码,启动pprof服务器并在本地8080端口进行访问:

import "net/http"
import _ "net/http/pprof"

func main() {
    go func() {
        http.ListenAndServe("localhost:8080", nil)
    }()
    // ...
}

启动pprof服务器之后,可以访问http://localhost:8080/debug/pprof/heap进行堆内存分析,获取内存占用的堆快照。

上图中框出的这 4 个部分应该是平时最常用的,从上往下分别是:
阻塞分析:    比如,goroutine 的 wait。
内存分析:    比如,内存泄漏、内存消耗异常等情况。
互斥锁分析:    比如,观察代码里用到的sync.RWMutex 和 sync.Mutex 的具体情况。
CPU 分析:    比如,排查哪些代码较多地占用了 CPU 资源。

以下是一段消耗内存的代码,用于走读流程,如下:

func main() {
	go func() { http.ListenAndServe("0.0.0.0:8899", nil) }()
	str := "gejigejigejigejigejigejigejigjeijgiewjiasdiahdkuhakudsfhakdshgkjasdkjgbakjsdfioajewoiepqbibgijqbgoipbjwebkjfqjkw egqhwejbgfaijwebgjqhb"
	for i := 0; i < 999; i++ {
		str += str
	}
	fmt.Scanln()
}

在 web 页面点击「heap」入口,我们可以看到实时内存的使用情况。

再通过以下命令进入到命令交互模式看看效果:

go tool pprof http://localhost:8899/debug/pprof/heap

进去之后输入「top」,就能很直观的看到哪个方法占用了内存。

这里的几个列的含义简单罗列下:

  • flat:当前函数所占用的容量。
  • flat%:当前函数所占用的容量,在总分配容量的百分比。
  • sum%:是从调用的最外层到当前方法累加使用的容量占总容量的百分比
  • cum:当前函数以及子函数所占用的容量。
  • cum%:当前函数以及子函数所占用的容量,在总分配容量的百分比。
  • 最后一列是函数的名字

可以再输入获得更详细的信息:
list main.main 

上述指令会罗列出每行代码占用的容量;其他类似,如下是分析CPU的

2. Go Memstats

Go Memstats是一个Golang的标准库工具,用于收集和显示内存信息。可以在代码中导入“runtime”包,并调用“runtime.ReadMemStats(&mem)”函数收集内存信息。然后可以打印内存使用情况的详情,例如总分配量和分配的堆块数量等。以下是示例代码:

import (
    "fmt"
    "runtime"
)

func main() {
    var mem runtime.MemStats
    runtime.ReadMemStats(&mem)
    fmt.Printf("TotalAlloc (Heap) = %v MiB\n", mem.TotalAlloc/mb)
    fmt.Printf("Alloc = %v MiB\n", mem.Alloc/mb)
    fmt.Printf("Sys = %v MiB\n", mem.Sys/mb)
    fmt.Printf("NumGC = %v\n", mem.NumGC)
}

这将输出程序中已分配的总内存,已使用的内存量,系统内存使用情况和垃圾回收次数等信息。使用这些信息,可以分析内存占用量的增长和优化内存使用。

3. 程序 crash 的时候自动创建 dump 文件

大多数时候,我们可能没有条件实时分析程序运行情况。比如问题在生产环境偶发出现,且无法在测试环境重现。

这个时候我们可以配置当程序 crash 的时候自动保存 dump 文件。

先输入命令「ulimit -a」看下当前是否开启了core file。

这里的数字单位是 block,具体需要根据所在的操作系统一个 block 对应的大小来设置。如果是0的话说明未开启。可以通过:

ulimit -c 1024 或者 ulimit -c unlimited 来设置 dump 文件的最大 size。

如果想要永久有效,则:

echo "ulimit -c unlimited" >> ~/.profile

让程序发生 crash 的时候会自动生成 dump 文件

## 临时有效

export GOBACTRACE=crash

## 永久有效

echo "export GOTRACEBACK=crash " >> ~/.profile

这就意味着,让程序发生 crash 的时候会自动生成 dump 文件。有了 dump 文件,可以用 gdb 或者 delve 工具来分析了(官方更建议我们使用 delve,对 Golang 的支持更好)

总之,无论你使用哪种工具,分析Golang内存溢出问题需要耐心和技巧。建议不仅可以使用这些工具进行数据分析,同时还应该阅读关于垃圾回收机制和内存管理的文档,尝试实现更好的编码习惯,以减少内存溢出的可能性。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Golang中,内存溢出通常发生在程序使用了过多的内存而无法进行正确的垃圾回收。这可能是由于以下几个原因引起的: 1. 无限循环:如果你的程序进入了一个无限循环,没有终止条件,那么它会持续消耗内存直到溢出。确保你的循环有正确的终止条件。 2. 递归调用:如果你的程序中存在无限递归调用,每次递归调用都会在堆栈中创建新的帧,最终导致堆栈溢出。确保你的递归调用有正确的终止条件。 3. 大量数据:如果你的程序处理大量数据并将其存储在内存中,可能会导致内存溢出。考虑使用流式处理或分批处理来减少内存使用量。 4. 内存泄漏:如果你的程序中存在内存泄漏,即分配的内存没有被正确释放,随着时间的推移会导致内存溢出。确保你在使用完内存后及时释放它们。 为了解决内存溢出问题,你可以尝试以下方法: 1. 优化代码:检查你的代码是否存在无限循环或无限递归调用,并确保使用合适的数据结构和算法来减少内存使用量。 2. 减少数据量:如果可能的话,尝试减少处理的数据量,例如使用分页处理数据或只加载必要的部分数据。 3. 内存分析工具:使用Golang的内存分析工具来检测和定位内存泄漏问题,例如go tool pprof和go-torch。 4. 增加内存限制:如果你的程序确实需要处理大量数据,可以考虑增加操作系统对程序的内存限制,但这不是一个长期解决方案,应该尽量避免过度依赖内存。 需要注意的是,Golang在内存管理方面相对于其他一些编程语言来说更加高效,但仍然需要开发者小心处理内存使用,以避免内存溢出问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dkjhl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值