This repository has been archived by the owner on Aug 6, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
memory.js
113 lines (102 loc) · 3.84 KB
/
memory.js
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
// record memory usage each minute and run a periodic gc(), keeping cpu
// usage within allowable range of 1ms per minute. Also, dynamically
// adjust the period between gc()'s to reach a target of the gc saving
// 4 megabytes each time.
let MemoryMonitor
const oneMinute = 60 * 1000
const oneMegaByte = 1024 * 1024
let CpuTimeBucket = 100 // current cpu time allowance in milliseconds
const CpuTimeBucketMax = 100 // maximum amount of cpu time allowed in bucket
const CpuTimeBucketRate = 10 // add this many milliseconds per minute
let gcInterval = 1 // how many minutes between gc (parameter is dynamically adjusted)
let countSinceLastGc = 0 // how many minutes since last gc
const MemoryChunkSize = 4 // how many megabytes we need to free to consider gc worth doing
const readyToGc = function() {
// update allowed cpu time
CpuTimeBucket = CpuTimeBucket + CpuTimeBucketRate
CpuTimeBucket =
CpuTimeBucket < CpuTimeBucketMax ? CpuTimeBucket : CpuTimeBucketMax
// update counts since last gc
countSinceLastGc = countSinceLastGc + 1
// check there is enough time since last gc and we have enough cpu
return countSinceLastGc > gcInterval && CpuTimeBucket > 0
}
const executeAndTime = function(fn) {
// time the execution of fn() and subtract from cpu allowance
const t0 = process.hrtime()
fn()
const dt = process.hrtime(t0)
const timeTaken = (dt[0] + dt[1] * 1e-9) * 1e3 // in milliseconds
CpuTimeBucket -= Math.ceil(timeTaken)
return timeTaken
}
const inMegaBytes = function(obj) {
// convert process.memoryUsage hash {rss,heapTotal,heapFreed} into megabytes
const result = {}
for (const k in obj) {
const v = obj[k]
result[k] = (v / oneMegaByte).toFixed(2)
}
return result
}
const updateMemoryStats = function(oldMem, newMem) {
countSinceLastGc = 0
const delta = {}
for (const k in newMem) {
delta[k] = (newMem[k] - oldMem[k]).toFixed(2)
}
// take the max of all memory measures
const savedMemory = Math.max(-delta.rss, -delta.heapTotal, -delta.heapUsed)
delta.megabytesFreed = savedMemory
// did it do any good?
if (savedMemory < MemoryChunkSize) {
gcInterval = gcInterval + 1 // no, so wait longer next time
} else {
gcInterval = Math.max(gcInterval - 1, 1) // yes, wait less time
}
return delta
}
module.exports = MemoryMonitor = {
monitor(logger) {
const interval = setInterval(() => MemoryMonitor.Check(logger), oneMinute)
const Metrics = require('./index')
return Metrics.registerDestructor(() => clearInterval(interval))
},
Check(logger) {
let mem
const Metrics = require('./index')
const memBeforeGc = (mem = inMegaBytes(process.memoryUsage()))
Metrics.gauge('memory.rss', mem.rss)
Metrics.gauge('memory.heaptotal', mem.heapTotal)
Metrics.gauge('memory.heapused', mem.heapUsed)
Metrics.gauge('memory.gc-interval', gcInterval)
// Metrics.gauge("memory.cpu-time-bucket", CpuTimeBucket)
logger.log(mem, 'process.memoryUsage()')
if (global.gc != null && readyToGc()) {
const gcTime = executeAndTime(global.gc).toFixed(2)
const memAfterGc = inMegaBytes(process.memoryUsage())
const deltaMem = updateMemoryStats(memBeforeGc, memAfterGc)
logger.log(
{
gcTime,
memBeforeGc,
memAfterGc,
deltaMem,
gcInterval,
CpuTimeBucket
},
'global.gc() forced'
)
// Metrics.timing("memory.gc-time", gcTime)
Metrics.gauge('memory.gc-rss-freed', -deltaMem.rss)
Metrics.gauge('memory.gc-heaptotal-freed', -deltaMem.heapTotal)
return Metrics.gauge('memory.gc-heapused-freed', -deltaMem.heapUsed)
}
}
}