修复miniblink一处内存泄漏的bug
最后的结论很简单,是我绑定v8 function的时候没释放。但查找问题的过程比较艰难,因为
v8的代码实在太难读了。
下面先大概了解下v8的垃圾回收机制。
v8\src\global-handles.cc里有个GlobalHandles类,管理了所有使用v8::Persistent<XXX>形式声明的永久性
v8变量。说永久性的意思是,除非自己主动调用v8::Persistent::reset,不然就一直占稳不释放了。
但有个例外,就是v8::Persistent::SetWeak形式设置了释放回调,或者叫弱回调(这里有点存疑)。
看段堆栈:
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::FunctionWrappernode.dll!WTF::PartBoundFunctionImplnode.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里。
当垃圾回收的时刻到来时,
> 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对象。这过程我没怎么看懂,因为都是类似
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来标记的。下个断点,
> 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++
其实关键就是这句
v8::internal::MarkCompactMarkingVisitor::VisitPointers
v8构造了一个图结构,通过不停遍历里面的节点,如果访问到的节点,就标记为block。
那回到最上面的问题,
一个object可以放在多个 node。如果一个node没人引用了,会被标记为white。这是下面堆栈做的:
> 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:
> node.dll!v8::internal::MarkBit::Set()node.dll!v8::internal::Marking::WhiteToBlacknode.dll!v8::internal::MarkCompactCollector::SetMarknode.dll!v8::internal::RootMarkingVisitor::MarkObjectByPointernode.dll!v8::internal::RootMarkingVisitor::VisitPointernode.dll!v8::internal::GlobalHandles::IterateStrongRootsnode.dll!v8::internal::Heap::IterateStrongRootsnode.dll!v8::internal::MarkCompactCollector::MarkRootsnode.dll!v8::internal::MarkCompactCollector::MarkLiveObjectsnode.dll!v8::internal::MarkCompactCollector::CollectGarbagenode.dll!v8::internal::Heap::MarkCompactnode.dll!v8::internal::Heap::PerformGarbageCollectionnode.dll!v8::internal::Heap::CollectGarbagenode.dll!v8::internal::Heap::CollectAllAvailableGarbagenode.dll!v8::Isolate::LowMemoryNotificationnode.dll!content::BlinkPlatformImpl::doGarbageCollectednode.dll!content::BlinkPlatformImpl::garbageCollectedTimernode.dll!blink::Timer
最后跟踪到这里时,才发现miniblink是因为同个object被两个node引用导致没释放。问题解决。
修复miniblink一处内存泄漏的bug相关推荐
- 无处不在的内存泄漏-苹果BUG?
即使你对自己的技术功底有再多的自信,都请养成使用Instruments工具排查内存泄漏的良好习惯, 即使Instruments再牛逼,你也还要养成看接口文档的良好习惯,因为你防谁也防不了苹果... 下 ...
- Android内存泄漏leakcanary2.7
一.内存泄漏 1.1 内存泄漏简介 内存泄漏,是指一些对象已经不再需要,但是无法成功被gc回收,导致这部分内存无法释放,造成资源的浪费.当大量的内存泄漏堆积时,严重时还容易间接引发OOM. 例 ...
- “因为内存泄漏,我的 M1 MacBook Pro 瘫痪了”
上个月,苹果在宣布"王炸候场中"后,19 号发布了 AirPods 3.HomePod mini 等新品,而这场发布会真正的主角当属新款 MacBook Pro:强悍的 M1 Pr ...
- java内存泄漏案例_寻找内存泄漏:一个案例研究
java内存泄漏案例 一周前,我被要求修复一个有内存泄漏问题的webapp. 考虑到过去两年左右的时间里我已经看到并修复了数百个泄漏,我想这有多难. 但是事实证明这是一个挑战. 12小时后,我发现该应 ...
- 寻找内存泄漏:一个案例研究
一周前,我被要求修复一个有内存泄漏问题的webapp. 考虑到过去两年左右的时间里我已经看到并修复了数百个泄漏,我想这有多难. 但是事实证明这是一个挑战. 12小时后,我发现该应用程序中不少于5个漏洞 ...
- RT-Thread 内存泄漏分析利器 memtrace+ramdump
嵌入式开发中,我们经常遇到内存泄漏的问题,却无从下手,使用RT-Thread memtrace 和ramdump功能可以轻松查看内存泄漏 memtrace的使用本文不过多描述,详情请参考 https: ...
- Delphi XE 利用FastMM4检测内存泄漏的设置
1.在项目中使用FastMM4 打开项目文件,让第一个单元引用FastMM4. 2.定义编译条件变量 Shift+Ctrl+F11,打开项目设置窗口,设置Conditional defines. 定义 ...
- 一篇文章搞定《Android内存泄漏》
------<Android内存泄漏> 什么是内存泄漏 常见的内存泄漏以及规避方式 单例模式引用Activity 非静态内部类 注册的反注册 定时器Timer WebView的内存泄漏 资 ...
- Android之内存泄漏以及解决办法(持更)
Android之内存泄漏以及解决办法 文章链接:http://blog.csdn.net/qq_16628781/article/details/67761590 知识点: 单例造成的内存泄漏原因和解 ...
最新文章
- list extend 和 append
- 利用Vlan控制与隔离广播风暴
- dedephp geteditor(,cms教程:dedecms修改后台编辑器参数GetEditor的方法
- 基础知识(9)- Swing用户界面组件
- 《C#编程风格》还记得多少
- java 动态队列_RabbitMq之动态修改队列参数
- 2020年中国OTT大屏服务行业研究报告
- 雷军:电视机越大才越舒服!
- linux系统shell脚本编程,Linux系统shell脚本编程(一)
- 针对业务系统的开发,如何做需求分析和设计1
- 大数据标签获取处理步骤_盘点大数据处理引擎
- Visual Studio 2010 SP1将支持HTML5和CSS3
- matlab循环取出矩阵的某一行并标示上A1 A2 A3
- 物料编码,使用有意思的编码还是无意义的编码呢?
- 《白帽子讲web安全》第一篇 世界观安全
- wifi 小米pro 驱动 黑苹果_小米Pro 15.6英寸(i7 8550U-MX110)游戏本黑苹果
- 2022年强网杯rcefile wp
- APP支付和H5网页支付有哪些不同?
- 《联邦学习实战》杨强 读书笔记十七——联邦学习加速方法
- CSS : 七彩呼吸灯
热门文章
- 用命令:tar -zxvf,解压tar.gz包失败的问题解决。
- Spring Webflux - 01 MVC的困境
- 小时候的超级玛丽,开发需要的所有资源
- 移动端h5落地页总结(vue cli+vant)
- Python求解多机系统暂态分析
- 扩展欧几里得算法、ax+by=c求解、ax≡c(mod m)、逆元求解、(b/a)%m计算c++代码
- Hibernate+Struts2进行数据的修改
- 机器学习实践:基于支持向量机算法对鸢尾花进行分类
- WebRTC系列-工具系列之音频混音
- QPushButton 设置背景颜色