某进程内存占用突增问题排查

实习时,某版本需要上线前,自测某进程内存突然飙高

首先引入pprof性能侦测工具

具体问题定位

1.goroutine泄露在这里插入图片描述

发现某函数下有chanrecv没有关闭,定位到代码发现有一个管道在连接中断后没有正确关闭。
解决方法:在连接关闭函数中添加管道关闭逻辑

2.内存增高

pprof分析工具中看见的内存大小与top命令中看见的内存大小差距较大。查询资料显示可能存在的情况是:
1.大量内存GC了但是没有返回给OS,可能有内核态操作(比如I/O)消耗了大量内存。
2.pprof采样是以512KB单位大小采样,可能有大量不同的方法占用内存<512KB,碎片内存被使用但是并没有被采集到。
3.golang惰性回收问题,go1.12-1.15版本中采用惰性回收,有内存gc了但是没有返还给OS
排除情况3,golang已经升级到1.18
排除情况2,在top中观察到占用已经有200MB之后还是无法观察到内存占用,不可能有大量不同的方法,并且每个方法占用内存小于512KB
排除情况1,如果有内核态操作出现了I/O,在pprof中也应当由用户态的函数开启系统调用,应该也能够捕获到内核态操作消耗的大量内存。

后来发现top中能发现两个相同名称的进程在运行
使用ps aux |grep "进程名"之后发现,该进程启动了子进程,主进程用于充当守护进程,子进程用于处理连接。直接pprof是配置在子进程代码段中,并没有过高的占用,主进程占用过高。所以重新配置pprof在主进程代码段中。
观察到主进程的堆占用确实持续升高。
在这里插入图片描述

cmd:=exec.Command(args)
//定位到代码段,该子进程是通过主进程使用cmd.start()来运行的。
//通过声明一个变量来存储子进程的stderr
var tmp Bytes.Buffer
cmd.Stderr=&tmp
cmd.Start()
cmd.Wait()

问题发现,就是因为子进程的err一直在输出,让tmp变量一直囤积,而子进程是持续运行的,主进程会在wait 处持续等待子进程运行,无法将该变量释放。该变量一直会囤积子进程的stderr输出,导致内存增大。
以前没有发现该问题是因为子进程stderr输出较少,而新版本中加入了端口探活,导致stderr量增多,使问题暴露出来。

解决方法:采用流式传输

stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("Error creating StdoutPipe:", err)
		return
	}

	err = cmd.Start()
	if err != nil {
		fmt.Println("Error starting command:", err)
		return
	}

	// 在后台持续读取输出,但不做任何处理
	go func() {
		buffer := make([]byte, 4096)
		for {
			_, err := stdout.Read(buffer)
			if err != nil {
				break
			}
		}
	}()
	cmd.Wait()

解决该问题

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值