>  기사  >  백엔드 개발  >  대형 개체에서 메모리 해제

대형 개체에서 메모리 해제

PHPz
PHPz앞으로
2024-02-09 09:03:09734검색

대형 개체에서 메모리 해제

PHP 편집기 Yuzi가 메모리 사용을 최적화하는 기술, 즉 대형 개체에서 메모리를 해제하는 기술을 소개합니다. 개발 과정에서 대규모 배열이나 대규모 데이터베이스 쿼리 결과와 같은 대형 개체를 생성하는 경우가 많으며 이러한 개체는 많은 메모리 리소스를 차지합니다. 이러한 객체 사용이 끝나면 제때에 메모리를 해제하는 것이 좋은 프로그래밍 습관입니다. 이 기사에서는 애플리케이션 성능과 효율성을 향상시키기 위해 대형 개체에서 메모리를 확보하는 방법을 보여줍니다.

질문 내용

이해할 수 없는 내용을 접했습니다. 모두가 도울 수 있기를 바랍니다!

자원:

  1. https://medium.com/@chaewonkong/solving-memory-leak-issues-in-go-http-clients-ba0b04574a83
  2. https://www.golinuxcloud.com/golang-garbage-collector/

더 이상 필요하지 않은 큰 슬라이스와 맵(이것은 모든 참조 유형에 적용되는 것 같습니다)을 nil로 설정하여 GC 작업을 단순화할 수 있다는 몇 가지 기사를 읽었습니다. 다음은 제가 읽은 예 중 하나입니다.

으아악

제가 이해한 바에 따르면, 함수 processresponse 完成时,data 变量将超出范围,基本上将不再存在。然后,gc 将验证是否没有对 []byte 切片(data가 슬라이스에 대한 참조를 가리키면 메모리가 지워집니다.

datadata 设置为 nil로 설정 가비지 수집을 개선하는 방법은 무엇입니까?

감사합니다!

해결 방법

다른 사람들이 이미 지적했듯이: 반환하기 전에 data = nil 不会改变 gc 方面的任何内容。 go 编译器将应用优化,并且 golang 的垃圾收集器在不同的阶段工作。用最简单的术语(有许多遗漏和过度简化):设置 data = nil을 설정하고 기본 슬라이스에 대한 모든 참조를 제거해도 더 이상 참조되지 않는 원자 스타일 메모리 릴리스가 트리거되지 않습니다. 슬라이스가 더 이상 참조되지 않으면 해당 슬라이스는 그렇게 표시되고 다음 스캔까지 연관된 메모리가 해제되지 않습니다.

가비지 수집은 모든 사용 사례에 대해 최상의 결과를 생성하는 최적의 솔루션이 있는 종류의 문제가 아니기 때문에 어려운 문제입니다. Go 런타임은 수년에 걸쳐 많이 발전했으며 중요한 작업은 런타임 가비지 수집기에서 수행됩니다. 그 결과, 드문 경우지만 간단한 somevar = nil만으로도 눈에 띄는 차이는 물론 작은 차이도 만들 수 있습니다.

가비지 수집(또는 일반적인 런타임 메모리 관리)과 관련된 런타임 오버헤드에 영향을 미칠 수 있는 몇 가지 간단한 경험 법칙 유형 팁을 찾고 있다면 이 문장이 귀하의 질문에 있는 내용을 모호하게 다루고 있는 것 같다는 것을 알고 있습니다. 질문:

대형 슬라이스와 매핑을 설정하여 GC 작업을 단순화할 수 있다고 제안됩니다

이렇게 하면 코드를 분석할 때 중요한 결과를 얻을 수 있습니다. 처리해야 하는 많은 양의 데이터를 읽거나 다른 종류의 일괄 작업을 수행하고 슬라이스를 반환해야 한다고 가정하면 사람들이 다음과 같이 작성하는 것은 드문 일이 아닙니다.

으아악

코드를 다음과 같이 변경하면 쉽게 최적화할 수 있습니다.

으아악

첫 번째 구현에서는 lencap 为 0 创建一个切片。第一次调用 append를 사용할 때 슬라이스의 현재 용량을 초과하여 런타임에서 메모리를 할당하게 됩니다. 여기에 설명된 대로 새 용량 계산은 매우 간단하며 메모리가 할당되고 데이터가 복사됩니다.

으아악

기본적으로 추가할 조각이 가득 찰 때마다(즉, len == cap)调用 append 时,您将分配一个可容纳: (len + 1) * 2 元素的新切片。知道在第一个示例中 datalencap == 0부터 시작하면 이것이 무엇을 의미하는지 살펴보겠습니다.

으아악

슬라이스의 데이터 구조가 큰 경우(예: 많은 중첩 구조, 많은 간접 참조 등) 이렇게 잦은 재할당 및 복사로 인해 비용이 상당히 많이 들 수 있습니다. 코드에 이러한 루프가 많이 포함되어 있으면 pprof에 표시되기 시작합니다(호출에 시간이 많이 걸리기 시작합니다 gcmalloc). 또한 15개의 입력 값을 처리하는 경우 데이터 조각은 다음과 같이 표시됩니다.

으아악

즉, 15개만 필요할 때 30개의 값에 메모리를 할당하고 해당 메모리를 점차적으로 더 큰 4개의 청크로 할당하여 각 재할당 시 데이터를 복사한다는 의미입니다.

반대로, 두 번째 구현에서는 루프 전에 다음과 같은 데이터 슬라이스를 할당합니다.

으아악

한 번 할당되므로 재할당 및 복사가 필요하지 않으며 반환된 슬라이스는 메모리 공간의 절반을 차지합니다. 이런 의미에서 우리는 처음에 더 큰 메모리 블록을 할당하여 나중에 필요한 증분 할당 및 복사 호출 수를 줄이며 전체적으로 런타임 비용을 줄입니다.

如果我不知道需要多少内存怎么办

这是一个公平的问题。这个例子并不总是适用。在这种情况下,我们知道需要多少个元素,并且可以相应地分配内存。有时,世界并不是这样运作的。如果您不知道最终需要多少数据,那么您可以:

  1. 做出有根据的猜测: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,您应该对行数有一个可用的、稍微保守的估计。在该代码末尾添加一些日志记录,比较上限与估计值,然后您可以相应地调整您的预测计算。
  2. 分析您的代码。有时,您会发现自己正在开发新功能或全新项目,而您没有历史数据可以依靠进行猜测。在这种情况下,您可以简单地猜测,运行一些测试场景,或者启动一个测试环境来提供您的代码生产数据版本并分析代码。当您正在主动分析一两个切片/映射的内存使用/运行时成本时,我必须强调这是优化。仅当这是瓶颈或明显问题时(例如,运行时内存分配阻碍了整体分析),您才应该在这方面花费时间。在绝大多数情况下,这种级别的优化将牢牢地属于微优化的范畴。 坚持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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 stackoverflow.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제