You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-11-25 22:41:46 +02:00
```go
type interInst struct {
x int
}
type inter interface {
}
var sink []inter
func BenchmarkX(b *testing.B) {
sink = make([]inter, b.N)
for i := 0; i < b.N; i++ {
sink[i] = &interInst{}
}
clear(sink)
sink = sink[:0]
runtime.GC()
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
b.Log(b.N, ms.Frees) // Frees is the cumulative count of heap objects freed.
}
```
```
clear:
ioz_test.go:35: 1 589
ioz_test.go:35: 100 711
ioz_test.go:35: 10000 10729
ioz_test.go:35: 1000000 1010750 <-- 1m+ freed
ioz_test.go:35: 16076874 17087643
ioz_test.go:35: 19514749 36602412
```
```
no clear:
ioz_test.go:35: 1 585
ioz_test.go:35: 100 606
ioz_test.go:35: 10000 725
ioz_test.go:35: 1000000 10745 <-- some "overhead" objects freed, not the slice.
ioz_test.go:35: 16391445 1010765
ioz_test.go:35: 21765238 17402230
```
This is documented at https://go.dev/wiki/SliceTricks:
> NOTE If the type of the element is a pointer or a struct with pointer
fields, which need to be garbage collected, the above implementations of
Cut and Delete have a potential memory leak problem: some elements with
values are still referenced by slice a’s underlying array, just not
“visible” in the slice. Because the “deleted” value is referenced in the
underlying array, the deleted value is still “reachable” during GC, even
though the value cannot be referenced by your code. If the underlying
array is long-lived, this represents a leak.
Followed by examples of how zeroing out the slice elements solves
the problem. This PR does the same.
44 lines
1.1 KiB
Go
44 lines
1.1 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"go.opentelemetry.io/otel/sdk/metric/exemplar"
|
|
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
)
|
|
|
|
var exemplarPool = sync.Pool{
|
|
New: func() any { return new([]exemplar.Exemplar) },
|
|
}
|
|
|
|
func collectExemplars[N int64 | float64](out *[]metricdata.Exemplar[N], f func(*[]exemplar.Exemplar)) {
|
|
dest := exemplarPool.Get().(*[]exemplar.Exemplar)
|
|
defer func() {
|
|
clear(*dest) // Erase elements to let GC collect objects.
|
|
*dest = (*dest)[:0]
|
|
exemplarPool.Put(dest)
|
|
}()
|
|
|
|
*dest = reset(*dest, len(*out), cap(*out))
|
|
|
|
f(dest)
|
|
|
|
*out = reset(*out, len(*dest), cap(*dest))
|
|
for i, e := range *dest {
|
|
(*out)[i].FilteredAttributes = e.FilteredAttributes
|
|
(*out)[i].Time = e.Time
|
|
(*out)[i].SpanID = e.SpanID
|
|
(*out)[i].TraceID = e.TraceID
|
|
|
|
switch e.Value.Type() {
|
|
case exemplar.Int64ValueType:
|
|
(*out)[i].Value = N(e.Value.Int64())
|
|
case exemplar.Float64ValueType:
|
|
(*out)[i].Value = N(e.Value.Float64())
|
|
}
|
|
}
|
|
}
|