-
Notifications
You must be signed in to change notification settings - Fork 0
/
gc.go
84 lines (71 loc) · 2.12 KB
/
gc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package weakmap
import (
"runtime"
"runtime/metrics"
)
// Refer to https://go.dev/doc/gc-guide.
// And, https://pkg.go.dev/runtime/metrics#Value.
type memStats struct {
samples []metrics.Sample
total uint64
heapReleased uint64
used uint64
numCycles uint64
}
// pressure computes a number between 0 and 1 that represents the memory pressure.
// "total" is selected as memory mapped to the process. In the future we may
// want to use to the minimum of the system memory or GOMEMLIMIT, as Go will
// ask for far less memory than it actually needs.
func (m *memStats) pressure() float64 {
return (float64(m.used)) / (float64(m.total))
}
func (m *memStats) read() {
if m.samples == nil {
m.samples = []metrics.Sample{
{Name: "/memory/classes/total:bytes"},
{Name: "/memory/classes/heap/released:bytes"},
{Name: "/memory/classes/heap/objects:bytes"},
{Name: "/gc/cycles/total:gc-cycles"},
}
}
metrics.Read(m.samples)
m.total = m.samples[0].Value.Uint64()
m.heapReleased = m.samples[1].Value.Uint64()
m.used = m.samples[2].Value.Uint64()
m.numCycles = m.samples[3].Value.Uint64()
}
// initFinalizerChain sets up a finalizer to run the GC, and then recreates itself
// upon ever GC.
func (l *Map[K, V]) initFinalizerChain() {
// This sentinel value escapes into the heap and is used to "hook" into
// the GC.
sentinel := allocSentinel()
defer runtime.KeepAlive(sentinel)
runtime.SetFinalizer(sentinel, func(s *gcSentinel) {
l.mu.Lock()
defer l.mu.Unlock()
l.gc(s)
// IMPORTANT: do not create a finalizer when the map is empty
// to avoid an infinite leak of the sentinel object.
if len(l.index) > 0 {
l.initFinalizerChain()
}
})
}
func (l *Map[K, V]) gc(s *gcSentinel) {
l.lastSentinel = s
l.gcMemStats.read()
// memPressure is used to determine the probability of evicting an entry.
var (
memPressure = l.gcMemStats.pressure()
toEvict = memPressure * float64(len(l.index))
)
// fmt.Printf("pressure: %0.2f, toEvict: %0.2f\n", memPressure, toEvict)
for i := 0; i <= int(toEvict); i++ {
last := l.lruList.Tail()
if last == nil {
return
}
l.delete(last.Data.key)
}
}