| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- /*
- * 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.debug(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.debug(
- {
- 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)
- }
- },
- }
|