memory.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /*
  2. * decaffeinate suggestions:
  3. * DS102: Remove unnecessary code created because of implicit returns
  4. * DS207: Consider shorter variations of null checks
  5. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  6. */
  7. // record memory usage each minute and run a periodic gc(), keeping cpu
  8. // usage within allowable range of 1ms per minute. Also, dynamically
  9. // adjust the period between gc()'s to reach a target of the gc saving
  10. // 4 megabytes each time.
  11. let MemoryMonitor
  12. const oneMinute = 60 * 1000
  13. const oneMegaByte = 1024 * 1024
  14. let CpuTimeBucket = 100 // current cpu time allowance in milliseconds
  15. const CpuTimeBucketMax = 100 // maximum amount of cpu time allowed in bucket
  16. const CpuTimeBucketRate = 10 // add this many milliseconds per minute
  17. let gcInterval = 1 // how many minutes between gc (parameter is dynamically adjusted)
  18. let countSinceLastGc = 0 // how many minutes since last gc
  19. const MemoryChunkSize = 4 // how many megabytes we need to free to consider gc worth doing
  20. const readyToGc = function () {
  21. // update allowed cpu time
  22. CpuTimeBucket = CpuTimeBucket + CpuTimeBucketRate
  23. CpuTimeBucket =
  24. CpuTimeBucket < CpuTimeBucketMax ? CpuTimeBucket : CpuTimeBucketMax
  25. // update counts since last gc
  26. countSinceLastGc = countSinceLastGc + 1
  27. // check there is enough time since last gc and we have enough cpu
  28. return countSinceLastGc > gcInterval && CpuTimeBucket > 0
  29. }
  30. const executeAndTime = function (fn) {
  31. // time the execution of fn() and subtract from cpu allowance
  32. const t0 = process.hrtime()
  33. fn()
  34. const dt = process.hrtime(t0)
  35. const timeTaken = (dt[0] + dt[1] * 1e-9) * 1e3 // in milliseconds
  36. CpuTimeBucket -= Math.ceil(timeTaken)
  37. return timeTaken
  38. }
  39. const inMegaBytes = function (obj) {
  40. // convert process.memoryUsage hash {rss,heapTotal,heapFreed} into megabytes
  41. const result = {}
  42. for (const k in obj) {
  43. const v = obj[k]
  44. result[k] = (v / oneMegaByte).toFixed(2)
  45. }
  46. return result
  47. }
  48. const updateMemoryStats = function (oldMem, newMem) {
  49. countSinceLastGc = 0
  50. const delta = {}
  51. for (const k in newMem) {
  52. delta[k] = (newMem[k] - oldMem[k]).toFixed(2)
  53. }
  54. // take the max of all memory measures
  55. const savedMemory = Math.max(-delta.rss, -delta.heapTotal, -delta.heapUsed)
  56. delta.megabytesFreed = savedMemory
  57. // did it do any good?
  58. if (savedMemory < MemoryChunkSize) {
  59. gcInterval = gcInterval + 1 // no, so wait longer next time
  60. } else {
  61. gcInterval = Math.max(gcInterval - 1, 1) // yes, wait less time
  62. }
  63. return delta
  64. }
  65. module.exports = MemoryMonitor = {
  66. monitor(logger) {
  67. const interval = setInterval(() => MemoryMonitor.Check(logger), oneMinute)
  68. const Metrics = require('./index')
  69. return Metrics.registerDestructor(() => clearInterval(interval))
  70. },
  71. Check(logger) {
  72. let mem
  73. const Metrics = require('./index')
  74. const memBeforeGc = (mem = inMegaBytes(process.memoryUsage()))
  75. Metrics.gauge('memory.rss', mem.rss)
  76. Metrics.gauge('memory.heaptotal', mem.heapTotal)
  77. Metrics.gauge('memory.heapused', mem.heapUsed)
  78. Metrics.gauge('memory.gc-interval', gcInterval)
  79. // Metrics.gauge("memory.cpu-time-bucket", CpuTimeBucket)
  80. logger.debug(mem, 'process.memoryUsage()')
  81. if (global.gc != null && readyToGc()) {
  82. const gcTime = executeAndTime(global.gc).toFixed(2)
  83. const memAfterGc = inMegaBytes(process.memoryUsage())
  84. const deltaMem = updateMemoryStats(memBeforeGc, memAfterGc)
  85. logger.debug(
  86. {
  87. gcTime,
  88. memBeforeGc,
  89. memAfterGc,
  90. deltaMem,
  91. gcInterval,
  92. CpuTimeBucket,
  93. },
  94. 'global.gc() forced'
  95. )
  96. Metrics.timing('memory.gc-time', +gcTime)
  97. Metrics.gauge('memory.gc-rss-freed', -deltaMem.rss)
  98. Metrics.gauge('memory.gc-heaptotal-freed', -deltaMem.heapTotal)
  99. return Metrics.gauge('memory.gc-heapused-freed', -deltaMem.heapUsed)
  100. }
  101. },
  102. }