Open smallnewer opened 7 years ago
最近追到线上内存泄露,是自行包装的计时器导致的。
原因是:本身开了几个V8 Isolate,内部又不得不用定时器。所以又基于libuv封装了一个简易的定时器。C++那边的clearTimeout没时间搞定,当时就紧急用JS封装了一版支持一下,其中就利用了JS对象保存了定时器状态。然而在执行回调后忘记了从该对象上delete,导致内存会慢慢的涨。
其实是有两个问题,一个是内存泄露,一个是线上内存表现的抖动过大,瞬间几十M上下,不像一个健康的表现。
本身利用对象保存,就比较占内存,加上对GC的影响,导致压测的时候内存表现抖动非常大,正好趁着这次机会,把这里也给优化掉。利用ArrayBuffer或者Nodejs的Buffer都可以,不占用V8 HEAP,也不影响GC。
改完后又有一个新的问题,之前一直对Number和Object对V8 GC的影响是否有明显差距有疑惑(即是否将所有的HiddenClass替换成Number有必要,这对我的另一个想法有比较大的影响)
Number和Object对V8 GC的影响是否有明显差距
于是正好测了一下。
// 每秒创建10W个定时器,持续1秒(外部没有保留句柄) // Number实现:100~250M浮动 // Object实现:120~400M浮动 // Node.js实现:上下20M浮动 // 每秒创建10W个定时器,持续0.001秒(外部没有保留句柄) // Number实现:79~87M浮动 // Object实现:79~87M浮动
之所以会出现上面的情况,大约是对象保留时间长后进入到老生代,而Number和Object在老生代里的GC代价有所差异。 正常情况下,外部大部分都有保留句柄,这会增加GC的代价。
所以利用Number还是有一定的内存优势。但Object带来的可维护性会更好。
上面补充了一个Nodejs实现的测试,它的定时器确实内存表现非常好。但我还不确定是其经过优化的定时器导致的,还是对V8的GC策略有自己的配置导致的。(我个人更倾向后者)将来有时间会替换过去。不过鉴于时间问题,用golang实现一个timer给JS用可能是又快又好的方式。
补充: 经过验证,Nodejs的MarkSweep确实比我封装的V8 Isolate的要触发频繁,内存占用基本都是1W个/2M,由于Node触发的更频繁,所以每秒10W个,1秒触发一次GC就是20M上下。而改成100W/每秒,则是200M上下浮动。但是为何触发频繁的原因还没找到。
准备升级到最新版本再看看。
同时发现一个东西:http://v8project.blogspot.com/2015/09/custom-startup-snapshots.html 可以考虑用来加密源码
最近追到线上内存泄露,是自行包装的计时器导致的。
原因是:本身开了几个V8 Isolate,内部又不得不用定时器。所以又基于libuv封装了一个简易的定时器。C++那边的clearTimeout没时间搞定,当时就紧急用JS封装了一版支持一下,其中就利用了JS对象保存了定时器状态。然而在执行回调后忘记了从该对象上delete,导致内存会慢慢的涨。
其实是有两个问题,一个是内存泄露,一个是线上内存表现的抖动过大,瞬间几十M上下,不像一个健康的表现。
本身利用对象保存,就比较占内存,加上对GC的影响,导致压测的时候内存表现抖动非常大,正好趁着这次机会,把这里也给优化掉。利用ArrayBuffer或者Nodejs的Buffer都可以,不占用V8 HEAP,也不影响GC。
改完后又有一个新的问题,之前一直对
Number和Object对V8 GC的影响是否有明显差距
有疑惑(即是否将所有的HiddenClass替换成Number有必要,这对我的另一个想法有比较大的影响)于是正好测了一下。
之所以会出现上面的情况,大约是对象保留时间长后进入到老生代,而Number和Object在老生代里的GC代价有所差异。 正常情况下,外部大部分都有保留句柄,这会增加GC的代价。
所以利用Number还是有一定的内存优势。但Object带来的可维护性会更好。
上面补充了一个Nodejs实现的测试,它的定时器确实内存表现非常好。但我还不确定是其经过优化的定时器导致的,还是对V8的GC策略有自己的配置导致的。(我个人更倾向后者)将来有时间会替换过去。不过鉴于时间问题,用golang实现一个timer给JS用可能是又快又好的方式。
补充: 经过验证,Nodejs的MarkSweep确实比我封装的V8 Isolate的要触发频繁,内存占用基本都是1W个/2M,由于Node触发的更频繁,所以每秒10W个,1秒触发一次GC就是20M上下。而改成100W/每秒,则是200M上下浮动。但是为何触发频繁的原因还没找到。