PHP 편집기 Yuzi가 메모리 사용을 최적화하는 기술, 즉 대형 개체에서 메모리를 해제하는 기술을 소개합니다. 개발 과정에서 대규모 배열이나 대규모 데이터베이스 쿼리 결과와 같은 대형 개체를 생성하는 경우가 많으며 이러한 개체는 많은 메모리 리소스를 차지합니다. 이러한 객체 사용이 끝나면 제때에 메모리를 해제하는 것이 좋은 프로그래밍 습관입니다. 이 기사에서는 애플리케이션 성능과 효율성을 향상시키기 위해 대형 개체에서 메모리를 확보하는 방법을 보여줍니다.
이해할 수 없는 내용을 접했습니다. 모두가 도울 수 있기를 바랍니다!
자원:
더 이상 필요하지 않은 큰 슬라이스와 맵(이것은 모든 참조 유형에 적용되는 것 같습니다)을 nil
로 설정하여 GC 작업을 단순화할 수 있다는 몇 가지 기사를 읽었습니다. 다음은 제가 읽은 예 중 하나입니다.
제가 이해한 바에 따르면, 함수 processresponse
完成时,data
变量将超出范围,基本上将不再存在。然后,gc 将验证是否没有对 []byte
切片(data
가 슬라이스에 대한 참조를 가리키면 메모리가 지워집니다.
data
를 data
设置为 nil
로 설정 가비지 수집을 개선하는 방법은 무엇입니까?
감사합니다!
data = nil
不会改变 gc 方面的任何内容。 go 编译器将应用优化,并且 golang 的垃圾收集器在不同的阶段工作。用最简单的术语(有许多遗漏和过度简化):设置 data = nil
을 설정하고 기본 슬라이스에 대한 모든 참조를 제거해도 더 이상 참조되지 않는 원자 스타일 메모리 릴리스가 트리거되지 않습니다. 슬라이스가 더 이상 참조되지 않으면 해당 슬라이스는 그렇게 표시되고 다음 스캔까지 연관된 메모리가 해제되지 않습니다.
가비지 수집은 모든 사용 사례에 대해 최상의 결과를 생성하는 최적의 솔루션이 있는 종류의 문제가 아니기 때문에 어려운 문제입니다. Go 런타임은 수년에 걸쳐 많이 발전했으며 중요한 작업은 런타임 가비지 수집기에서 수행됩니다. 그 결과, 드문 경우지만 간단한 somevar = nil
만으로도 눈에 띄는 차이는 물론 작은 차이도 만들 수 있습니다.
가비지 수집(또는 일반적인 런타임 메모리 관리)과 관련된 런타임 오버헤드에 영향을 미칠 수 있는 몇 가지 간단한 경험 법칙 유형 팁을 찾고 있다면 이 문장이 귀하의 질문에 있는 내용을 모호하게 다루고 있는 것 같다는 것을 알고 있습니다. 질문:
대형 슬라이스와 매핑을 설정하여 GC 작업을 단순화할 수 있다고 제안됩니다
이렇게 하면 코드를 분석할 때 중요한 결과를 얻을 수 있습니다. 처리해야 하는 많은 양의 데이터를 읽거나 다른 종류의 일괄 작업을 수행하고 슬라이스를 반환해야 한다고 가정하면 사람들이 다음과 같이 작성하는 것은 드문 일이 아닙니다.
으아악코드를 다음과 같이 변경하면 쉽게 최적화할 수 있습니다.
으아악첫 번째 구현에서는 len
和 cap
为 0 创建一个切片。第一次调用 append
를 사용할 때 슬라이스의 현재 용량을 초과하여 런타임에서 메모리를 할당하게 됩니다. 여기에 설명된 대로 새 용량 계산은 매우 간단하며 메모리가 할당되고 데이터가 복사됩니다.
기본적으로 추가할 조각이 가득 찰 때마다(즉, len
== cap
)调用 append
时,您将分配一个可容纳: (len + 1) * 2
元素的新切片。知道在第一个示例中 data
以 len
和 cap
== 0부터 시작하면 이것이 무엇을 의미하는지 살펴보겠습니다.
슬라이스의 데이터 구조가 큰 경우(예: 많은 중첩 구조, 많은 간접 참조 등) 이렇게 잦은 재할당 및 복사로 인해 비용이 상당히 많이 들 수 있습니다. 코드에 이러한 루프가 많이 포함되어 있으면 pprof에 표시되기 시작합니다(호출에 시간이 많이 걸리기 시작합니다 gcmalloc
). 또한 15개의 입력 값을 처리하는 경우 데이터 조각은 다음과 같이 표시됩니다.
즉, 15개만 필요할 때 30개의 값에 메모리를 할당하고 해당 메모리를 점차적으로 더 큰 4개의 청크로 할당하여 각 재할당 시 데이터를 복사한다는 의미입니다.
반대로, 두 번째 구현에서는 루프 전에 다음과 같은 데이터 슬라이스를 할당합니다.
으아악한 번 할당되므로 재할당 및 복사가 필요하지 않으며 반환된 슬라이스는 메모리 공간의 절반을 차지합니다. 이런 의미에서 우리는 처음에 더 큰 메모리 블록을 할당하여 나중에 필요한 증분 할당 및 복사 호출 수를 줄이며 전체적으로 런타임 비용을 줄입니다.
这是一个公平的问题。这个例子并不总是适用。在这种情况下,我们知道需要多少个元素,并且可以相应地分配内存。有时,世界并不是这样运作的。如果您不知道最终需要多少数据,那么您可以:
不,将一个简单的切片变量设置为 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!