memory.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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 = CpuTimeBucket < CpuTimeBucketMax ? CpuTimeBucket : CpuTimeBucketMax;
  24. // update counts since last gc
  25. countSinceLastGc = countSinceLastGc + 1;
  26. // check there is enough time since last gc and we have enough cpu
  27. return (countSinceLastGc > gcInterval) && (CpuTimeBucket > 0);
  28. };
  29. const executeAndTime = function(fn) {
  30. // time the execution of fn() and subtract from cpu allowance
  31. const t0 = process.hrtime();
  32. fn();
  33. const dt = process.hrtime(t0);
  34. const timeTaken = (dt[0] + (dt[1]*1e-9)) * 1e3; // in milliseconds
  35. CpuTimeBucket -= Math.ceil(timeTaken);
  36. return timeTaken;
  37. };
  38. const inMegaBytes = function(obj) {
  39. // convert process.memoryUsage hash {rss,heapTotal,heapFreed} into megabytes
  40. const result = {};
  41. for (let k in obj) {
  42. const v = obj[k];
  43. result[k] = (v / oneMegaByte).toFixed(2);
  44. }
  45. return result;
  46. };
  47. const updateMemoryStats = function(oldMem, newMem) {
  48. countSinceLastGc = 0;
  49. const delta = {};
  50. for (let k in newMem) {
  51. delta[k] = (newMem[k] - oldMem[k]).toFixed(2);
  52. }
  53. // take the max of all memory measures
  54. const savedMemory = Math.max(-delta.rss, -delta.heapTotal, -delta.heapUsed);
  55. delta.megabytesFreed = savedMemory;
  56. // did it do any good?
  57. if (savedMemory < MemoryChunkSize) {
  58. gcInterval = gcInterval + 1; // no, so wait longer next time
  59. } else {
  60. gcInterval = Math.max(gcInterval - 1, 1); // yes, wait less time
  61. }
  62. return delta;
  63. };
  64. module.exports = (MemoryMonitor = {
  65. monitor(logger) {
  66. const interval = setInterval(() => MemoryMonitor.Check(logger)
  67. , oneMinute);
  68. const Metrics = require("./metrics");
  69. return Metrics.registerDestructor(() => clearInterval(interval));
  70. },
  71. Check(logger) {
  72. let mem;
  73. const Metrics = require("./metrics");
  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.log(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.log({gcTime, memBeforeGc, memAfterGc, deltaMem, gcInterval, CpuTimeBucket}, "global.gc() forced");
  86. //Metrics.timing("memory.gc-time", gcTime)
  87. Metrics.gauge("memory.gc-rss-freed", -deltaMem.rss);
  88. Metrics.gauge("memory.gc-heaptotal-freed", -deltaMem.heapTotal);
  89. return Metrics.gauge("memory.gc-heapused-freed", -deltaMem.heapUsed);
  90. }
  91. }
  92. });