import EventEmitter from 'events' import gc from 'gc-stats' import _ from 'exact-math' import dbfn from 'debounce-fn' import sizeof from 'object-sizeof' import { getHeapStatistics } from 'v8' const STATS = { total_heap_size: 'byte', total_heap_size_executable: 'byte', total_physical_size: 'byte', total_available_size: 'byte', used_heap_size: 'byte', heap_size_limit: 'byte', malloced_memory: 'byte', peak_malloced_memory: 'byte', does_zap_garbage: 'boolean', number_of_native_contexts: 'val', number_of_detached_contexts: 'val' } const PROPS = Object.keys(STATS) class Memory extends EventEmitter { constructor(opts={}) { super() this._props = Object.keys(this.stats) this._diff = {} PROPS.forEach(prop=>this._diff[prop]={}) this._current = this.get() this.db = opts.db || 3000 this.reset() this.last = this.first this.gc = gc() } get stats () { return getHeapStatistics() } sizeof (obj, u='M') { return mb(sizeof(obj)) } reset() { this.first=this.get() } get() { const mem = this.stats PROPS.forEach(prop => { if (STATS[prop] === 'byte') mem[prop] = mb(mem[prop],2) }) return mem } snapshot() { this.last=Object.assign({},this._current) this._current=this.get() this.remaining = _.sub(this._current.heap_size_limit,this._current.used_heap_size) PROPS.forEach(prop => { if (STATS[prop] !== 'boolean') { this._diff[prop].last = _.sub(this._current[prop],this.last[prop]) this._diff[prop].overall=_.sub(this._current[prop],this.first[prop]) } }) return { remaining:this.remaining, first:this.first, last:this.last, current:this._current, diff:this._diff } } log (opts) { this.snapshot() console.log(`============= ${opts.msg||''} =====================`,new Date().toLocaleString()) console.log(`remaining Heap: ${this.remaining} mb`) console.log(this._log('used_heap_size')) console.log(this._log('number_of_native_contexts')) console.log(`============= ${opts.msg||''} =====================`) } _log (prop) { return `${prop} > \ diff:${this._diff[prop].overall}, \ lastdiff:${this._diff[prop].last}, \ current:${this._current[prop]}, \ last:${this.last[prop]}, \ first:${this.first[prop]}, \ ` } // totalHeapSize: 17911808, // totalHeapExecutableSize: 573440, // usedHeapSize: 13410032, // heapSizeLimit: 2197815296, // totalPhysicalSize: 17675040, // totalAvailableSize: 2184093576, // mallocedMemory: 8192, // peakMallocedMemory: 897248, // numberOfNativeContexts: 3, // numberOfDetachedContexts: 0 watch (handler) { gc().on('stats', dbfn( stats => { const diff = _.sub(stats.after.heapSizeLimit,stats.after.usedHeapSize) if (stats.gctype > 0) { if (!handler) { console.log(stats.gctype,'----------- Heap at GC ------------',new Date().toLocaleString()) console.log(`Heap> \ remaining: ${mb(diff)} mb \ before:${mb(stats.before.usedHeapSize)}, \ after:${mb(stats.after.usedHeapSize)} mb, \ diff:${mb(stats.diff.usedHeapSize)} mb \ `) console.log('-----------------------') } else handler(stats) }}, {wait:this.db} ) )} } // end class function mb (val,p=2) { // console.log(val,val / 1024 / 1024, _.round(val / 1024 / 1024, -p) ) return _.round(val / 1024 / 1024, -p) } export default Memory export { Memory }