从大对象中释放内存
php小编柚子为您介绍一种优化内存使用的技巧——从大对象中释放内存。在开发过程中,我们经常会创建一些大对象,比如大数组或大型数据库查询结果,这些对象会占用大量内存资源。当我们使用完这些对象后,及时释放内存是一种良好的编程习惯。本文将向您展示如何从大对象中释放内存,以提高应用程序的性能和效率。
问题内容
我遇到了一些我不明白的事情。希望大家帮忙!
资源:
- https://medium.com/@chaewonkong/solving-memory-leak-issues-in-go-http-clients-ba0b04574a83
- https://www.golinuxcloud.com/golang-garbage-collector/
我在几篇文章中读到建议,在我们不再需要它们之后,我们可以通过将大切片和映射(我想这适用于所有引用类型)设置为 nil
来简化 gc 的工作。这是我读过的示例之一:
func ProcessResponse(resp *http.Response) error { data, err := ioutil.ReadAll(resp.Body) if err != nil { return err } // Process data here data = nil // Release memory return nil }
据我了解,当函数 processresponse
完成时,data
变量将超出范围,基本上将不再存在。然后,gc 将验证是否没有对 []byte
切片(data
指向的切片)的引用,并将清除内存。
将 data
设置为 data
设置为 nil
如何改进垃圾收集?
谢谢!
解决方法
正如其他人已经指出的那样:在返回之前设置data = nil
不会改变 gc 方面的任何内容。 go 编译器将应用优化,并且 golang 的垃圾收集器在不同的阶段工作。用最简单的术语(有许多遗漏和过度简化):设置 data = nil
,并删除对底层切片的所有引用不会触发不再引用的内存的原子样式释放。一旦切片不再被引用,它就会被标记为这样,并且关联的内存直到下一次扫描才会被释放。
垃圾收集是一个难题,很大程度上是因为它不是那种具有能为所有用例产生最佳结果的最佳解决方案的问题。多年来,go 运行时已经发展了很多,重要的工作正是在运行时垃圾收集器上完成的。结果是,在极少数情况下,简单的 somevar = nil
会产生哪怕很小的差异,更不用说明显的差异了。
如果您正在寻找一些简单的经验法则类型提示,这些提示可能会影响与垃圾收集(或一般的运行时内存管理)相关的运行时开销,我确实知道这句话似乎模糊地涵盖了一个在你的问题中:
建议我们可以通过设置大切片和映射来简化 gc 的工作
在分析代码时,这可以产生显着的结果。假设您正在读取需要处理的大量数据,或者您必须执行某种其他类型的批处理操作并返回切片,那么人们编写这样的内容并不罕见:
func processstuff(input []sometypes) []resulttypes { data := []resulttypes{} for _, in := range input { data = append(data, processt(in)) } return data }
通过将代码更改为以下内容可以很容易地优化:
func processstuff(input []sometypes) []resulttypes { data := make([]resulttypes, 0, len(input)) // set cap for _, in := range input { data = append(data, processt(in)) } return data }
第一个实现中发生的情况是,您使用 len
和 cap
为 0 创建一个切片。第一次调用 append
时,您超出了切片的当前容量,这将导致运行时分配内存。正如此处所解释的,新容量的计算相当简单,内存被分配,数据被分配复制过来:
t := make([]byte, len(s), (cap(s)+1)*2) copy(t, s)
本质上,每次当要附加的切片已满时(即 len
== cap
)调用 append
时,您将分配一个可容纳: (len + 1) * 2
元素的新切片。知道在第一个示例中 data
以 len
和 cap
== 0 开头,让我们看看这意味着什么:
1st iteration: append creates slice with cap (0+1) *2, data is now len 1, cap 2 2nd iteration: append adds to data, now has len 2, cap 2 3rd iteration: append allocates a new slice with cap (2 + 1) *2, copies the 2 elements from data to this slice and adds the third, data is now reassigned to a slice with len 3, cap 6 4th-6th iterations: data grows to len 6, cap 6 7th iteration: same as 3rd iteration, although cap is (6 + 1) * 2, everything is copied over, data is reassigned a slice with len 7, cap 14
如果切片中的数据结构较大(即许多嵌套结构、大量间接寻址等),那么这种频繁的重新分配和复制可能会变得相当昂贵。如果您的代码包含大量此类循环,它将开始显示在 pprof 中(您将开始看到花费大量时间调用 gcmalloc
)。此外,如果您正在处理 15 个输入值,您的数据切片最终将如下所示:
dataslice { len: 15 cap: 30 data underlying_array[30] }
这意味着您将为 30 个值分配内存,而您只需要 15 个值,并且您将将该内存分配为 4 个逐渐增大的块,并在每次重新分配时复制数据。
相比之下,第二个实现将在循环之前分配一个如下所示的数据片:
data { len: 0 cap: 15 data underlying_array[15] }
它是一次性分配的,因此不需要重新分配和复制,并且返回的切片将占用一半的内存空间。从这个意义上说,我们首先在开始时分配更大的内存块,以减少稍后所需的增量分配和复制调用的数量,这总体上会降低运行时成本。
如果我不知道需要多少内存怎么办
这是一个公平的问题。这个例子并不总是适用。在这种情况下,我们知道需要多少个元素,并且可以相应地分配内存。有时,世界并不是这样运作的。如果您不知道最终需要多少数据,那么您可以:
- 做出有根据的猜测:gc 很困难,而且与您不同的是,编译器和 go 运行时缺乏模糊逻辑,人们必须提出现实、合理的猜测。有时它会像这样简单:“嗯,我从该数据源获取数据,我们只存储最后 n 个元素,所以最坏的情况下,我将处理 n 个元素” em>,有时它有点模糊,例如:您正在处理包含 sku、产品名称和库存数量的 csv。您知道 sku 的长度,可以假设库存数量为 1 到 5 位数字之间的整数,产品名称平均为 2-3 个单词长。英文单词的平均长度为 6 个字符,因此您可以粗略地了解 csv 行由多少字节组成:假设 sku == 10 个字符,80 个字节,产品描述 2.5 * 6 * 8 = 120 个字节,以及 ~ 4 个字节表示库存计数 + 2 个逗号和一个换行符,平均预期行长度为 207 个字节,为了谨慎起见,我们将其称为 200。统计输入文件,将其大小(以字节为单位)除以 200,您应该对行数有一个可用的、稍微保守的估计。在该代码末尾添加一些日志记录,比较上限与估计值,然后您可以相应地调整您的预测计算。
- 分析您的代码。有时,您会发现自己正在开发新功能或全新项目,而您没有历史数据可以依靠进行猜测。在这种情况下,您可以简单地猜测,运行一些测试场景,或者启动一个测试环境来提供您的代码生产数据版本并分析代码。当您正在主动分析一两个切片/映射的内存使用/运行时成本时,我必须强调这是优化。仅当这是瓶颈或明显问题时(例如,运行时内存分配阻碍了整体分析),您才应该在这方面花费时间。在绝大多数情况下,这种级别的优化将牢牢地属于微优化的范畴。 坚持80-20原则
回顾
不,将一个简单的切片变量设置为 nil 在 99% 的情况下不会产生太大影响。创建和附加到地图/切片时,更可能产生影响的是通过使用 make()
+ 指定合理的 cap
值来减少无关分配。其他可以产生影响的事情是使用指针类型/接收器,尽管这是一个需要深入研究的更复杂的主题。现在,我只想说,我一直在开发一个代码库,该代码库必须对远远超出典型 uint64
范围的数字进行操作,不幸的是,我们必须能够以更精确的方式使用小数比 float64
将允许。我们通过使用像 holiman/uint256 这样的东西解决了 uint64
问题,它使用指针接收器,并解决shopspring/decimal 的十进制问题,它使用值接收器并复制所有内容。在花费大量时间优化代码之后,我们已经达到了使用小数时不断复制值的性能影响已成为问题的地步。看看这些包如何实现加法等简单操作,并尝试找出哪个操作成本更高:
// original a, b := 1, 2 a += b // uint256 version a, b := uint256.NewUint(1), uint256.NewUint(2) a.Add(a, b) // decimal version a, b := decimal.NewFromInt(1), decimal.NewFromInt(2) a = a.Add(b)
这些只是我在最近的工作中花时间优化的几件事,但从中得到的最重要的一点是:
过早的优化是万恶之源
当您处理更复杂的问题/代码时,您需要花费大量精力来研究切片或映射的分配周期,因为潜在的瓶颈和优化需要付出很大的努力。您可以而且可以说应该采取措施避免过于浪费(例如,如果您知道所述切片的最终长度是多少,则设置切片上限),但您不应该浪费太多时间手工制作每一行,直到该代码的内存占用尽可能小。成本将是:代码更脆弱/更难以维护和阅读,整体性能可能会恶化(说真的,你可以相信 go 运行时会做得很好),大量的血、汗和泪水,以及急剧下降在生产力方面。
以上是从大对象中释放内存的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

华为手机内存不足已经成为很多用户面临的一个常见问题、随着移动应用和媒体文件的增加。帮助用户充分利用手机的存储空间、本文将介绍一些实用方法来解决华为手机内存不足的问题。1.清理缓存:历史记录以及无效数据,以释放内存空间,清除应用程序产生的临时文件。在华为手机设置中找到“存储”点击,选项“清除缓存”按钮即可删除应用程序的缓存文件。2.卸载不常用的应用程序:以释放内存空间,删除一些不常使用的应用程序。拖动到手机屏幕上方的、长按要删除的应用图标“卸载”然后点击确认按钮即可完成卸载,标志处。3.移动应用到

本地微调 DeepSeek 类模型面临着计算资源和专业知识不足的挑战。为了应对这些挑战,可以采用以下策略:模型量化:将模型参数转换为低精度整数,减少内存占用。使用更小的模型:选择参数量较小的预训练模型,便于本地微调。数据选择和预处理:选择高质量的数据并进行适当的预处理,避免数据质量不佳影响模型效果。分批训练:对于大数据集,分批加载数据进行训练,避免内存溢出。利用 GPU 加速:利用独立显卡加速训练过程,缩短训练时间。

写在前面&出发点端到端的范式使用统一的框架在自动驾驶系统中实现多任务。尽管这种范式具有简单性和清晰性,但端到端的自动驾驶方法在子任务上的性能仍然远远落后于单任务方法。同时,先前端到端方法中广泛使用的密集鸟瞰图(BEV)特征使得扩展到更多模态或任务变得困难。这里提出了一种稀疏查找为中心的端到端自动驾驶范式(SparseAD),其中稀疏查找完全代表整个驾驶场景,包括空间、时间和任务,无需任何密集的BEV表示。具体来说,设计了一个统一的稀疏架构,用于包括检测、跟踪和在线地图绘制在内的任务感知。此外,重

1、首先,进入Edge浏览器点击右上角三个点。2、然后,在任务栏中选择【扩展】。3、接着,将不需要使用的插件关闭或者卸载即可。

我们熟悉的Meta推出的Llama3、MistralAI推出的Mistral和Mixtral模型以及AI21实验室推出的Jamba等开源大语言模型已经成为OpenAI的竞争对手。在大多数情况下,用户需要根据自己的数据对这些开源模型进行微调,才能充分释放模型的潜力。在单个GPU上使用Q-Learning对比小的大语言模型(如Mistral)进行微调不是难事,但对像Llama370b或Mixtral这样的大模型的高效微调直到现在仍然是一个挑战。因此,HuggingFace技术主管PhilippSch

根据TrendForce的调查报告显示,AI浪潮对DRAM内存和NAND闪存市场带来明显影响。在本站5月7日消息中,TrendForce集邦咨询在今日的最新研报中称该机构调升本季度两类存储产品的合约价格涨幅。具体而言,TrendForce原先预估2024年二季度DRAM内存合约价上涨3~8%,现估计为13~18%;而在NAND闪存方面,原预估上涨13~18%,新预估为15~20%,仅eMMC/UFS涨幅较低,为10%。▲图源TrendForce集邦咨询TrendForce表示,该机构原预计在连续

是的,总体而言,Win11 比 Win10 占用更少的内存。优化措施包括:更轻量的系统内核、更好的内存管理、新的休眠选项和减少后台进程。测试表明,在类似配置下,Win11 的内存占用通常比 Win10 低 5-10%。但内存占用也受硬件配置、应用程序和系统设置的影响。
