现在虽然把mb的v8版本升级到了7.5.但这玩意目前发现有个重大的小问题:https://element.eleme.cn/#/zh-CN/component/button这里面的按钮,过了几分钟就点不动了。
也就是没响应消息了。
调试了一番后,发现是如下这段代码就可以重现:
<script>
var timerFunc;
//var port1;
function flushCallbacks () {
console.log("flushCallbacks");
}
function initTimer() {
var messageChannel = new MessageChannel;
var port2 = messageChannel.port2;
//port1 = messageChannel.port1;
messageChannel.port1.onmessage = flushCallbacks;
timerFunc = function() {
console.log("postMessage"); // weolar
port2.postMessage(1);
}
}
initTimer();
timerFunc();
</script>
在7.5v8中,会发现port1过不久就被析构了。
那为啥低版本v8不会呢?
对比了下新版本chromium,我看的是一头雾水。新版本chromium的gc比老版本复杂多了。貌似引入多代GC的概念。
只看到blink里有什么地方引用了这个port1..具体来说,是ThreadHeap::AdvanceMarking的marking_worklist_引用了。但这个marking_worklist_又是如何判断是否引入,我还没搞明白。
再看回老版本blink,终于发现了一点眉目。老版本blink,在MajorGCWrapperVisitor的VisitPersistentHandle,会遍历到这个port1.而这个遍历,是v8发起的,也是v8提到了变量。
整理了一下思路,大概搞明白了。v8对于这种native端的变量,会询问blink层。而blink层发现这玩意是个ActiveDOMObject对象,就会判断activeDOMObject->hasPendingActivity()。
老版本v8,我猜测是用m_isolate->SetObjectGroupId(*value, liveRootId());标识了这个对象在v8层不能被回收。然后到了gc的另外个阶段,也就是V8GCController::traceDOMWrappers这里面后,
v8会通知blink这个对象要做trace,也就是让blink不要回收。
而新版本v8,需要手动再调用tracer->RegisterEmbedderReference(tracedGlobal.Get());告诉v8.
我加上这个tracer->RegisterEmbedderReference(tracedGlobal.Get());后,貌似问题解决了。