158 lines
4.0 KiB
JavaScript
158 lines
4.0 KiB
JavaScript
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._diff = {}
|
|
PROPS.forEach(prop=>this._diff[prop]={})
|
|
this._current = this.get()
|
|
this.db = opts.db || 3000 // debounce for gc watch
|
|
this.reset()
|
|
this.last = this.first
|
|
this._watcher = false
|
|
}
|
|
|
|
get stats () {
|
|
return getHeapStatistics()
|
|
}
|
|
|
|
sizeof (obj, u='M') {
|
|
return this.mb(sizeof(obj))
|
|
}
|
|
|
|
reset() {
|
|
this.first=this.get()
|
|
}
|
|
|
|
get() {
|
|
const mem = this.stats
|
|
PROPS.forEach(prop => {
|
|
if (STATS[prop] === 'byte') mem[prop] = this.mb(mem[prop],2)
|
|
})
|
|
return mem
|
|
}
|
|
|
|
snapshot(props,str) {
|
|
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])
|
|
}
|
|
})
|
|
if (props) {
|
|
if (typeof props ==='string') {
|
|
if (props === 'all') props = PROPS
|
|
else props = [props]
|
|
}
|
|
const strs = props.map(prop => this.log(prop) +'\n')
|
|
return str ? strs.join('') : strs
|
|
}
|
|
else return { remaining:this.remaining, first:this.first, last:this.last, current:this._current, diff:this._diff }
|
|
}
|
|
|
|
clog (opts) {
|
|
let props = opts.props
|
|
if (!props) props = ['used_heap_size','number_of_native_contexts']
|
|
console.log(`============= ${opts.msg||''} =====================`,new Date().toLocaleString())
|
|
console.log(`\n remaining Heap: ${this.remaining} mb \n`)
|
|
console.log(this.snapshot(props,opts.str))
|
|
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]} \
|
|
`
|
|
}
|
|
|
|
subtract (first,second,raw) {
|
|
const diff = _.sub(first,second)
|
|
return raw ? diff : this.mb(diff)
|
|
}
|
|
|
|
mb (val,p=2) {
|
|
// console.log(val,val / 1024 / 1024, _.round(val / 1024 / 1024, -p) )
|
|
return _.round(val / 1024 / 1024, -p)
|
|
}
|
|
|
|
// totalHeapSize: 17911808,
|
|
// totalHeapExecutableSize: 573440,
|
|
// usedHeapSize: 13410032,
|
|
// heapSizeLimit: 2197815296,
|
|
// totalPhysicalSize: 17675040,
|
|
// totalAvailableSize: 2184093576,
|
|
// mallocedMemory: 8192,
|
|
// peakMallocedMemory: 897248,
|
|
// numberOfNativeContexts: 3,
|
|
// numberOfDetachedContexts: 0
|
|
|
|
watch (handler) {
|
|
if (this._watcher) {
|
|
console.log('already watching, unwatch first to reset a handler')
|
|
return
|
|
}
|
|
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: ${this.mb(diff)} mb \
|
|
before:${this.mb(stats.before.usedHeapSize)}, \
|
|
after:${this.mb(stats.after.usedHeapSize)} mb, \
|
|
diff:${this.mb(stats.diff.usedHeapSize)} mb \
|
|
`)
|
|
console.log('-----------------------')
|
|
}
|
|
else handler(stats)
|
|
}},
|
|
{wait:this.db}
|
|
)
|
|
)
|
|
this._watcher = true
|
|
}
|
|
|
|
|
|
unwatch() {
|
|
gc().removeAllListeners('stats')
|
|
this._watcher = false
|
|
}
|
|
|
|
|
|
} // end class
|
|
|
|
|
|
|
|
export default Memory
|
|
export { Memory }
|