当前位置: 首页 > article >正文

go语言内存泄漏的常见形式

go语言内存泄漏

在这里插入图片描述

子字符串导致的内存泄漏

使用自动垃圾回收的语言进行编程时,通常我们无需担心内存泄漏的问题,因为运行时会定期回收未使用的内存。但是如果你以为这样就完事大吉了,哪里就大错特措了。

因为,虽然go中并未对字符串时候共享底层内存块进行规定,但go语言编译器/运行时默认情况下允许字符串共享底层内存块,直到原先的字符串指向的内存被修改才会进行写时复制,这是一个很好的设计,既能节省内存,又能节省CPU资源,但有时也会导致"内存泄漏"。

例如如下代码,一旦调用demo就会导致将近1M内存的泄漏,因为s0只使用了50字节,但是会导致1M的内存一直无法被回收,这些内存会一直持续到下次s0被修改的时候才会被释放掉。

var s0 string // a package-level variable// A demo purpose function.
func f(s1 string) {s0 = s1[:50]// Now, s0 shares the same underlying memory block// with s1. Although s1 is not alive now, but s0// is still alive, so the memory block they share// couldn't be collected, though there are only 50// bytes used in the block and all other bytes in// the block become unavailable.
}func demo() {s := createStringWithLengthOnHeap(1 << 20) // 1M bytesf(s)
}

为了避免这种内存泄漏,我们可以使用[]byte来替代原先的1M大小的内存,不过这样会有两次50字节的内存重复

func f(s1 string) {s0 = string([]byte(s1[:50]))
}

当然我们也可以利用go编译器的优化来避免不必要的重复,只需要浪费一个字节内存就行

func f(s1 string) {s0 = (" " + s1[:50])[1:]
}

上述方法的缺点是编译器优化以后可能会失效,并且其他编译器可能无法提供该优化

避免此类内存泄漏的第三种方法是利用 Go 1.10 以来支持的 strings.Builder

import "strings"func f(s1 string) {var b strings.Builderb.Grow(50)b.WriteString(s1[:50])s0 = b.String()
}

从 Go 1.18 开始, strings 标准库包中新增了 Clone 函数,这成为了完成这项工作的最佳方式。

子切片导致的内存泄漏

同样场景下,切片也会导致内存的浪费

与子字符串类似,子切片也可能导致某种内存泄漏。在下面的代码中,调用 g 函数后,保存 s1 元素的内存块所占用的大部分内存将会丢失(如果没有其他值引用该内存块)。

var s0 []intfunc g(s1 []int) {// Assume the length of s1 is much larger than 30.s0 = s1[len(s1)-30:]
}

如果我们想避免这种内存泄漏,我们必须复制 s0 的 30 个元素,这样 s0 的活跃性就不会阻止收集承载 s1 元素的内存块。

func g(s1 []int) {s0 = make([]int, 30)copy(s0, s1[len(s1)-30:])// Now, the memory block hosting the elements// of s1 can be collected if no other values// are referencing the memory block.
}

未重置子切片指针导致的内存泄漏

在下面的代码中,调用 h 函数后,为切片 s 的第一个和最后一个元素分配的内存块将丢失。

func h() []*int {s := []*int{new(int), new(int), new(int), new(int)}// do something with s ...// 返回一个从1开始,不能到索引3的新切片, 也就是 s[1], s[2]return s[1:3:3]
}

只要返回的切片仍然有效,它就会阻止收集 s 的任何元素,从而阻止收集为 s 的第一个和最后一个元素引用的两个 int 值分配的两个内存块。

如果我们想避免这种内存泄漏,我们必须重置丢失元素中存储的指针。

func h() []*int {s := []*int{new(int), new(int), new(int), new(int)}// do something with s ...// Reset pointer values.s[0], s[len(s)-1] = nil, nilreturn s[1:3:3]
}

挂起Goroutine导致的内存泄漏

有时,Go 程序中的某些 goroutine 可能会永远处于阻塞状态。这样的 goroutine 被称为挂起的 goroutine。Go 运行时不会终止挂起的 goroutine,因此为挂起的 goroutine 分配的资源(以及它们引用的内存块)永远不会被垃圾回收。

Go 运行时不会杀死挂起的 Goroutine 有两个原因。一是 Go 运行时有时很难判断一个阻塞的 Goroutine 是否会被永久阻塞。二是我们有时会故意让 Goroutine 挂起。例如,有时我们可能会让 Go 程序的主 Goroutine 挂起,以避免程序退出。

如果不停止time.Ticker也会导致内存泄漏

time.Timer 值不再使用时,它会在一段时间后被垃圾回收。但 time.Ticker 值则不然。我们应该在 time.Ticker 值不再使用时停止它。

不正确地使用终结器会导致真正的内存泄漏

为属于循环引用组的成员值设置终结器(finalizer)可能会阻止为该循环引用组分配的所有内存块被回收。这是真正的内存泄漏,不是某种假象。

例如,在调用并退出以下函数后,分配给 xy 的内存块不能保证在未来的垃圾收集中被收集。

func memoryLeaking() {type T struct {v [1<<20]intt *T}var finalizer = func(t *T) {fmt.Println("finalizer called")}var x, y T// The SetFinalizer call makes x escape to heap.runtime.SetFinalizer(&x, finalizer)// The following line forms a cyclic reference// group with two members, x and y.// This causes x and y are not collectable.x.t, y.t = &y, &x // y also escapes to heap.
}

因此,请避免为循环引用组中的值设置终结器。

延迟函数调用导致的某种资源泄漏

非常大的延迟调用堆栈也可能会消耗大量内存,并且如果某些调用延迟太多,某些资源可能无法及时释放。

例如,如果在调用以下函数时需要处理许多文件,那么在函数退出之前将有大量文件处理程序无法释放。

func writeManyFiles(files []File) error {for _, file := range files {f, err := os.Open(file.path)if err != nil {return err}defer f.Close()_, err = f.WriteString(file.content)if err != nil {return err}err = f.Sync()if err != nil {return err}}return nil
}

对于这种情况,我们可以使用匿名函数来封装延迟调用,以便延迟函数调用能够更早地执行。例如,上面的函数可以重写并改进为

func writeManyFiles(files []File) error {for _, file := range files {if err := func() error {f, err := os.Open(file.path)if err != nil {return err}// The close method will be called at// the end of the current loop step.defer f.Close()_, err = f.WriteString(file.content)if err != nil {return err}return f.Sync()}(); err != nil {return err}}return nil
}

当然不要犯以下错误,需要有些同学将需要延时调用的函数字节省略,导致资源泄漏

_, err := os.Open(file.path)

如果是http请求,还会导致服务端挤压大量的连接无法释放

相关文章:

go语言内存泄漏的常见形式

go语言内存泄漏 子字符串导致的内存泄漏 使用自动垃圾回收的语言进行编程时&#xff0c;通常我们无需担心内存泄漏的问题&#xff0c;因为运行时会定期回收未使用的内存。但是如果你以为这样就完事大吉了&#xff0c;哪里就大错特措了。 因为&#xff0c;虽然go中并未对字符串…...

当DRAM邂逅SSD:新型“DRAM+”存储技术来了!

在当今快速发展的科技领域&#xff0c;数据存储的需求日益增长&#xff0c;对存储设备的性能和可靠性提出了更高的要求。传统DRAM以其高速度著称&#xff0c;但其易失性限制了应用范围&#xff1b;而固态硬盘SSD虽然提供非易失性存储&#xff0c;但在速度上远不及DRAM。 为了解…...

论文精度:YOLOMG:基于视觉的无人机间检测算法——外观与像素级运动融合详解

论文地址:https://arxiv.org/pdf/2503.07115 1. 论文概述 论文标题:YOLOMG: Vision-based Drone-to-Drone Detection with Appearance and Pixel-Level Motion Fusion 作者:Hanqing Guo, Xiuxiu Lin, Shiyu Zhao 发表:未明确会议/期刊(推测为预印本或待发表) 核心贡献:…...

JS实现文件点击或者拖拽上传

B站看到了渡一大师课的切片&#xff0c;自己实现了一下&#xff0c;做下记录 效果展示 分为上传前、上传中和上传后 实现 分为两步 界面交互网络请求 源码如下 upload.html <!DOCTYPE html> <html lang"zh-CN"><head><meta charset&q…...

【KWDB 创作者计划】_ruby基础语法

以下是 Ruby 基础语法的简明总结&#xff0c;适合快速入门&#xff1a; 一、变量与常量 1. 局部变量 小写字母或下划线开头&#xff0c;作用域为当前代码块。 name "Alice" _age 20//局部变量附加&#xff1a;{{{{ 声明与命名规则 命名格式 以小写字母或下划线…...

Python Cookbook-5.15 根据姓的首字母将人名排序和分组

任务 想将一组人名写入一个地址簿&#xff0c;同时还希望地址簿能够根据姓的首字母进行分组&#xff0c;且按照字母顺序表排序。 解决方案 Python 2.4 的新 itertools.groupby 函数使得这个任务很简单: import itertools def qroupnames(name_iterable):sorted_names sort…...

2025 蓝桥杯省赛c++B组个人题解

声明 本题解为退役蒻苟所写&#xff0c;不保证正确性&#xff0c;仅供参考。 花了大概2个半小时写完&#xff0c;感觉比去年省赛简单&#xff0c;难度大概等价于 codeforces dv4.5 吧 菜鸡不熟悉树上背包&#xff0c;调了一个多小时 题目旁边的是 cf 预测分 所有代码均以通…...

Centos7.9 升级内核,安装RTX5880驱动

系统镜像下载 https://vault.centos.org/7.9.2009/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso 系统安装步骤省略 开始安装显卡驱动 远程登录查看内核 [root192 ~]# uname -a Linux 192.168.119.166 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64 x8…...

Xdocreport实现根据模板导出word

只使用freemaker生成简单的word文档很容易&#xff0c;但是当word文档需要插入动态图片&#xff0c;带循环数据&#xff0c;且含有富文本时解决起来相对比较复杂&#xff0c;但是使用Xdocreport可以轻易解决。 Xdocreport既可以实现文档填充也可以实现文档转换&#xff0c;此处…...

运行一次性任务与定时任务

运行一次性任务与定时任务 文章目录 运行一次性任务与定时任务[toc]一、使用Job运行一次性任务1.创建一次性任务2.测试一次性任务3.删除Job 二、使用CronJob运行定时任务1.创建定时任务2.测试定时任务3.删除CronJob 一、使用Job运行一次性任务 1.创建一次性任务 &#xff08;…...

解决VS2022中scanf报错C4996

这个的原因是因为新版的VS认为scanf不安全&#xff0c;要去使用scanf_s&#xff0c;但在C语言中就需要scanf&#xff0c;所以我们只要以以下步骤解决就可以了。 只要加入宏定义即可 #define _CRT_SECURE_NO_WARNINGS 因为本人已经很少写小案例了&#xff0c;所以就用这个办法…...

当当平台商品详情接口设计与调用指南

当当平台商品详情接口设计与调用指南 接口名称 GET /api/product/detail 图书商品核心信息查询接口 请求参数说明 参数名称 类型 是否必填 说明 isbn string 是 国际标准书号(支持13位/10位) product_id string 否 平台内部商品编号&#xff08;与…...

sql server分析表大小

使用自动存储过程查询 EXEC sp_spaceused YourTableName; rows&#xff1a;表中的行数。reserved&#xff1a;表占用的总空间&#xff08;包括数据和索引&#xff09;。data&#xff1a;表数据占用的空间。index_size&#xff1a;索引占用的空间。unused&#xff1a;未使用的空…...

《AI大模型应知应会100篇》第13篇:大模型评测标准:如何判断一个模型的优劣

第13篇&#xff1a;大模型评测标准&#xff1a;如何判断一个模型的优劣 摘要 近年来&#xff0c;大语言模型&#xff08;LLMs&#xff09;在自然语言处理、代码生成、多模态任务等领域取得了显著进展。然而&#xff0c;随着模型数量和规模的增长&#xff0c;如何科学评估这些模…...

Linux基础9

一、日志管理 > 日志配置文件&#xff1a; > > ​ /var/log/messages #内核的消息以及各种服务的公共信息 > > ​ /var/log/dmesg #系统启动过程信息 > > ​ /var/log/cron #cron计划任务相关信息 > > ​ /var…...

hyper-v server服务器部署远程访问(我目前环境:hyper-v服务器+路由器+公网ip)

Hyper-v server部署(裸金属方式) 系统镜像下载安装# 下载地址:17763.737.190906-2324.rs5_release_svc_refresh_SERVERHYPERCORE_OEM_x64FRE_zh-cn_1.iso 安装的过程很简单,和安装Windows操作系统没啥区别,这里就不记录了。 安装过程可参考:安装Hyper-v Server 2016 部…...

【区块链安全 | 第三十七篇】合约审计之获取私有数据(一)

文章目录 私有数据访问私有数据实例存储槽Solidity 中的数据存储方式1. storage(持久化存储)定长数组变长数组2. memory(临时内存)3. calldata可见性关键字私有数据存储风险安全措施私有数据 私有数据(Private Data)通常指的是只对特定主体可见或可访问的数据,在区块链…...

项目管理(高软56)

系列文章目录 项目管理 文章目录 系列文章目录前言一、进度管理二、配置管理三、质量四、风险管理五、真题总结 前言 本节主要讲项目管理知识&#xff0c;这些知识听的有点意思啊。对于技术人想创业&#xff0c;单干的都很有必要听听。 一、进度管理 二、配置管理 三、质量 四…...

程序化广告行业(79/89):技术革新与行业发展脉络梳理

程序化广告行业&#xff08;79/89&#xff09;&#xff1a;技术革新与行业发展脉络梳理 大家好&#xff01;一直以来&#xff0c;我都热衷于在技术领域不断探索&#xff0c;也深知知识共享对于进步的重要性。写这篇博客&#xff0c;就是希望能和大家一起深入研究程序化广告行业…...

零基础上手Python数据分析 (13):DataFrame 数据合并与连接 - 整合多源数据,构建完整分析视图

写在前面 —— 告别 VLOOKUP 烦恼,掌握 Pandas 合并连接利器,轻松整合分散数据 在前面的博客中,我们学习了如何读取数据、清洗数据、选取数据。 现在,我们已经能够处理单个 DataFrame 中的数据了。 然而,在实际的数据分析项目中,数据往往不是存储在一个单独的文件或表格…...

解决OBS里的鼠标太小|OBS鼠标尺寸问题

在进行OBS录制时&#xff0c;不少用户可能会被鼠标显示问题所困扰。比如&#xff0c;录制时鼠标在画面中尺寸过大&#xff0c;影响视觉效果&#xff1b;或是出现两个鼠标指针&#xff0c;显得杂乱无章。其实&#xff0c;借助一款名为Custom cursor的工具&#xff0c;这些问题便…...

OpenCV边缘检测方法详解

文章目录 引言一、边缘检测基础概念边缘类型 二、OpenCV中的边缘检测方法1. Sobel算子2. Scharr算子3. Laplacian算子4. Canny边缘检测 三、性能比较与选择建议四、总结 引言 边缘检测是计算机视觉和图像处理中的基础技术&#xff0c;它能有效识别图像中物体的边界&#xff0c…...

寻找最大美丽数

# 输入&#xff1a;nums1 [4,2,1,5,3], nums2 [10,20,30,40,50], k 2 # 输出&#xff1a;[80,30,0,80,50] import random class Solution:def findMaxSum(self, nums1, nums2, k):hash_table []sum1 0data []print(**31,\n,\t数据)for key,values in enumerate(nums1):da…...

Linux:shell运行原理+权限

1.shell的运行原理 如果我们打开了命令终端或者是xshell进行远程登录服务器&#xff0c;就会看到命令行&#xff0c;如下图所示&#xff1a; 这个命令行本身也是系统中一个运行起来的程序&#xff0c;它用来接收用户的输入&#xff0c;帮用户来执行指令&#xff0c;将运行结果展…...

跨站请求是什么?

介绍 跨站请求&#xff08;Cross-Site Request&#xff09;通常是指浏览器在访问一个网站时&#xff0c;向另一个域名的网站发送请求的行为。这个概念在 Web 安全中非常重要&#xff0c;尤其是在涉及到“跨站请求伪造&#xff08;CSRF&#xff09;”和“跨域资源共享&#xff…...

【LeetCode Solutions】LeetCode 160 ~ 165 题解

CONTENTS LeetCode 160. 相交链表&#xff08;简单&#xff09;LeetCode 162. 寻找峰值&#xff08;中等&#xff09;LeetCode 164. 最大间距&#xff08;中等&#xff09;LeetCode 165. 比较版本号&#xff08;中等&#xff09; LeetCode 160. 相交链表&#xff08;简单&#…...

Openssl升级至openssl9.8p1含全部踩坑内容

1、安装依赖包基础条件 yum install gcc yum install gcc-c yum install perl yum install perl-IPC-Cmd yum install pam yum install pam-devel sudo yum install perl-Data-Dumper 问题一&#xff1a;提示yum不可用 镜像源问题更换阿里源即可 wget -O /etc/yum.repos.d/…...

ASP.NET Core 性能优化:内存缓存

文章目录 前言一、什么是缓存二、内存缓存三、使用内存缓存1&#xff09;注册内存缓存服务2&#xff09;注入与基本使用3&#xff09;高级用法GetOrCreate&#xff08;避免缓存穿透&#xff09;异步方法&#xff1a;GetOrCreateAsync&#xff08;避免缓存穿透&#xff09;两种过…...

二战蓝桥杯所感

&#x1f334; 前言 今天是2025年4月12日&#xff0c;第十六届蓝桥杯结束&#xff0c;作为二战的老手&#xff0c;心中还是颇有不甘的。一方面&#xff0c;今年的题目比去年简单很多&#xff0c;另一方面我感觉并没有把能拿的分都拿到手&#xff0c;这是我觉得最遗憾的地方。不…...

屏幕模块解析

通信协议 SPI 引脚定义 GPIO说明引脚配置SCK时钟线推挽输出MOSI主机输出、从机输入推挽输出MISO主机输入、从机输出浮空/上拉输入:没有开启数据传输时为高阻态SS片选推挽输出CPOL时钟极性0:空闲时SCK为低电平 1:空闲时SCK为高电平 CPHA时钟相位0:主从SCK第一个边沿输入1bi…...