前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >修复miniblink一处内存泄漏的bug

修复miniblink一处内存泄漏的bug

作者头像
龙泉寺扫地僧
发布2019-02-20 10:01:12
9650
发布2019-02-20 10:01:12
举报
文章被收录于专栏:盟主来了盟主来了

最后的结论很简单,是我绑定v8 function的时候没释放。但查找问题的过程比较艰难,因为

v8的代码实在太难读了。

下面先大概了解下v8的垃圾回收机制。

v8\src\global-handles.cc里有个GlobalHandles类,管理了所有使用v8::Persistent<XXX>形式声明的永久性

v8变量。说永久性的意思是,除非自己主动调用v8::Persistent::reset,不然就一直占稳不释放了。

但有个例外,就是v8::Persistent::SetWeak形式设置了释放回调,或者叫弱回调(这里有点存疑)。

看段堆栈:

代码语言:javascript
复制
        node.dll!v8::internal::GlobalHandles::NodeBlock::IncreaseUses	C++
 	node.dll!v8::internal::GlobalHandles::Node::IncreaseBlockUses	C++
 	node.dll!v8::internal::GlobalHandles::Node::Acquire	C++
 	node.dll!v8::internal::GlobalHandles::Create	C++
 	node.dll!v8::V8::GlobalizeReference	C++
 	node.dll!v8::PersistentBase<v8::ObjectTemplate>::New	C++
>	node.dll!v8::PersistentBase<v8::ObjectTemplate>::Reset<v8::ObjectTemplate>	C++
 	node.dll!blink::V8Window::getShadowObjectTemplate	C++
 	node.dll!blink::WindowProxy::createContext	C++
 	node.dll!blink::WindowProxy::initialize	C++
 	node.dll!blink::WindowProxy::initializeIfNeeded	C++
 	node.dll!blink::ScriptController::windowProxy	C++
 	node.dll!blink::LocalFrame::windowProxy	C++
 	node.dll!blink::toV8ContextEvenIfDetached	C++
 	node.dll!blink::ScriptState::forWorld	C++
 	node.dll!blink::ScriptState::forMainWorld	C++
 	node.dll!blink::WebLocalFrameImpl::mainWorldScriptContext	C++
 	node.dll!wke::CWebView::globalExec	C++
 	node.dll!wkeGlobalExec	C++
 	wkexe.exe!handleDocumentReady	C++
 	node.dll!wke::CWebWindow::_onDocumentReady	C++
 	node.dll!wke::CWebWindow::_staticOnDocumentReady	C++
 	node.dll!content::WebFrameClientImpl::didFinishDocumentLoad	C++
 	node.dll!blink::FrameLoaderClientImpl::dispatchDidFinishDocumentLoad	C++
 	node.dll!blink::FrameLoader::finishedParsing	C++
 	node.dll!blink::Document::finishedParsing	C++
 	node.dll!blink::HTMLConstructionSite::finishedParsing	C++
 	node.dll!blink::HTMLTreeBuilder::finished	C++
 	node.dll!blink::HTMLDocumentParser::end	C++
 	node.dll!blink::HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd	C++
 	node.dll!blink::HTMLDocumentParser::prepareToStopParsing	C++
 	node.dll!blink::HTMLDocumentParser::processParsedChunkFromBackgroundParser	C++
 	node.dll!blink::HTMLDocumentParser::pumpPendingSpeculations	C++
 	node.dll!blink::HTMLDocumentParser::resumeParsingAfterYield	C++
 	node.dll!blink::HTMLParserScheduler::continueParsing	C++
 	node.dll!WTF::FunctionWrapper
 	node.dll!WTF::PartBoundFunctionImpl
 	node.dll!blink::CancellableTaskFactory::CancellableTask::run	C++
 	node.dll!content::WebTimerBase::fired	C++
 	node.dll!content::WebThreadImpl::schedulerTasks	C++
 	node.dll!content::WebThreadImpl::fire	C++
 	node.dll!wkeWake	C++
 	wkexe.exe!RunMessageLoop	C++

这显示了如何把永久对象放到GlobalHandles里。

当垃圾回收的时刻到来时,

代码语言:javascript
复制
>	node.dll!v8::internal::GlobalHandles::IdentifyWeakHandles	C++
 	node.dll!v8::internal::MarkCompactCollector::MarkLiveObjects	C++
 	node.dll!v8::internal::MarkCompactCollector::CollectGarbage	C++
 	node.dll!v8::internal::Heap::MarkCompact	C++
 	node.dll!v8::internal::Heap::PerformGarbageCollection	C++
 	node.dll!v8::internal::Heap::CollectGarbage	C++
 	node.dll!v8::internal::Heap::CollectAllAvailableGarbage	C++
 	node.dll!v8::Isolate::LowMemoryNotification	C++
 	node.dll!content::BlinkPlatformImpl::doGarbageCollected	C++
 	node.dll!content::BlinkPlatformImpl::garbageCollectedTimer	C++
 	node.dll!blink::Timer<content::BlinkPlatformImpl>::fired	C++

可以看到GlobalHandles::IdentifyWeakHandles这里会遍历所有v8::Persistent永久化对象。

当设置了弱回调的时候,这些永久化对象就靠v8自己的垃圾处理机制了。此时就有个问题,v8如何知道这个对象无人引用了呢?

下面看几个v8的数据结构:

class GlobalHandles::Node

class Object

class  HeapObject

class Marking

要读懂垃圾回收机制,连得理清楚这几个对象的关系。

实际上是这样:

Object相对于对应js里的Object。不过注意下,v8分外部导出到头文件的Object和内部Object,这两货其实是一样的,只是

为了工程上的严谨性。

这个GlobalHandles::Node代表每个存在GlobalHandles里的对象,如上面那个持久化东西。Object也被存在这个node里。

但这里有个特别要强调的是,一个object可以放在多个 node。这点对后来解决内存泄漏比较关键。

每个object,其实是个以HeapObject开头的内存。这个HeapObject就是为了方便内存管理而设计的。

HeapObject的头部,通过一系列位运算,地址运算,得到了Marking对象。这过程我没怎么看懂,因为都是类似

代码语言:javascript
复制
static_cast<uint32_t>(addr - this->address()) >> kPointerSizeLog2;

这种风格的运算…实在不想搞明白算出真实Marking地址了。

总之得到Marking地址后,就可以通过Marking::IsBlack,Marking::IsWhite来判断Marking的状态(或者叫颜色)了。

black表示内存被占用,white表示内存没人占用。

(插一句,其实准确的说,应该是Marking里的cell类来记录这个颜色,不同object最后是在cell里标记的)

那问题来了,什么时候会去设置这些颜色呢?

最终是通过Marking::WhiteToBlack、Marking::MarkBlack来标记的。下个断点,

代码语言:javascript
复制
>	node.dll!v8::internal::Marking::WhiteToBlack	C++
 	node.dll!v8::internal::MarkCompactCollector::MarkObject	C++
 	node.dll!v8::internal::MarkCompactMarkingVisitor::MarkObjectByPointer	C++
 	node.dll!v8::internal::MarkCompactMarkingVisitor::VisitPointers	C++
 	node.dll!v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::VisitMap	C++
 	node.dll!v8::internal::StaticMarkingVisitor<v8::internal::MarkCompactMarkingVisitor>::IterateBody	C++
 	node.dll!v8::internal::MarkCompactCollector::EmptyMarkingDeque	C++
 	node.dll!v8::internal::MarkCompactCollector::ProcessMarkingDeque	C++
 	node.dll!v8::internal::MarkCompactCollector::PrepareForCodeFlushing	C++
 	node.dll!v8::internal::MarkCompactCollector::MarkLiveObjects	C++
 	node.dll!v8::internal::MarkCompactCollector::CollectGarbage	C++
 	node.dll!v8::internal::Heap::MarkCompact	C++
 	node.dll!v8::internal::Heap::PerformGarbageCollection	C++
 	node.dll!v8::internal::Heap::CollectGarbage	C++
 	node.dll!v8::internal::Heap::CollectAllAvailableGarbage	C++
 	node.dll!v8::Isolate::LowMemoryNotification	C++
 	node.dll!content::BlinkPlatformImpl::doGarbageCollected	C++

其实关键就是这句

代码语言:javascript
复制
v8::internal::MarkCompactMarkingVisitor::VisitPointers

v8构造了一个图结构,通过不停遍历里面的节点,如果访问到的节点,就标记为block。

那回到最上面的问题,

一个object可以放在多个 node。如果一个node没人引用了,会被标记为white。这是下面堆栈做的:

代码语言:javascript
复制
>	node.dll!v8::internal::Sweep<0,0,1,0>	C++
 	node.dll!v8::internal::MarkCompactCollector::SweepSpace	C++
 	node.dll!v8::internal::MarkCompactCollector::SweepSpaces	C++
 	node.dll!v8::internal::MarkCompactCollector::CollectGarbage	C++
 	node.dll!v8::internal::Heap::MarkCompact	C++
 	node.dll!v8::internal::Heap::PerformGarbageCollection	C++
 	node.dll!v8::internal::Heap::CollectGarbage	C++
 	node.dll!v8::internal::Heap::CollectAllAvailableGarbage	C++
 	node.dll!v8::Isolate::LowMemoryNotification	C++

但如果一个object放在两个node呢?比如有两个v8::Persistent<Objcet>持有了这个object,那就是一个object放在两个node里。

当第一个node在v8::internal::Sweep<0,0,1,0>里被标记white时,

下面堆栈可能又因为另个node在引用这个object,而导致同样的Marking又被标记成black:

代码语言:javascript
复制
>	node.dll!v8::internal::MarkBit::Set()
 	node.dll!v8::internal::Marking::WhiteToBlack
 	node.dll!v8::internal::MarkCompactCollector::SetMark
 	node.dll!v8::internal::RootMarkingVisitor::MarkObjectByPointer
 	node.dll!v8::internal::RootMarkingVisitor::VisitPointer
 	node.dll!v8::internal::GlobalHandles::IterateStrongRoots
 	node.dll!v8::internal::Heap::IterateStrongRoots
 	node.dll!v8::internal::MarkCompactCollector::MarkRoots
 	node.dll!v8::internal::MarkCompactCollector::MarkLiveObjects
 	node.dll!v8::internal::MarkCompactCollector::CollectGarbage
 	node.dll!v8::internal::Heap::MarkCompact
 	node.dll!v8::internal::Heap::PerformGarbageCollection
 	node.dll!v8::internal::Heap::CollectGarbage
 	node.dll!v8::internal::Heap::CollectAllAvailableGarbage
 	node.dll!v8::Isolate::LowMemoryNotification
 	node.dll!content::BlinkPlatformImpl::doGarbageCollected
 	node.dll!content::BlinkPlatformImpl::garbageCollectedTimer
 	node.dll!blink::Timer

最后跟踪到这里时,才发现miniblink是因为同个object被两个node引用导致没释放。问题解决。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年05月19日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档