电子说
webview;内存泄漏
问题现象:在 3.1release 版本和 3.2bete1 版本中,在 RK3568 上使用 etsWeb 和其他浏览器时,webview 所占的内存会随着使用而不断增大,最终导致浏览器 APP 因内存泄漏而崩溃。
在任意版本上使用浏览器 APP,可以长时间正常浏览网页。
在 3.1release 和 3.2beta1 上使用浏览器 APP,长时间浏览网页后,应用会崩溃。
foundationarkuiace_engineframeworkscorecomponentswebresourceweb_delegate.cpp
WebDelegate::~WebDelegate()
{
ReleasePlatformResource();
if (nweb_) {
nweb_- >OnDestroy();
}
}
//文件路径srcceflibcefbrowserosrrender_widget_host_view_osr.cc
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/touch_selection/touch_selection_controller.h"
// static
std::unordered_map< gfx::AcceleratedWidget, ui::Compositor* >
CefRenderWidgetHostViewOSR::compositor_map_;
std::unordered_map< gfx::AcceleratedWidget, uint32_t > CefRenderWidgetHostViewOSR::accelerate_widget_map_;
namespace {
// The maximum number of damage rects to cache for outstanding frame requests
// (for OnAcceleratedPaint).
const size_t kMaxDamageRects = 10;
compositor_- >SetRootLayer(root_layer_.get());
compositor_- >AddChildFrameSink(GetFrameSinkId());
#else
LOG(INFO) < < "compositor construct, widget = " < < static_cast< uint32_t >(browser_impl_- >GetAcceleratedWidget());
ui::Compositor* compositor = CefRenderWidgetHostViewOSR::GetCompositor(
browser_impl_- >GetAcceleratedWidget());
accelerate_widget_map_[browser_impl_- >GetAcceleratedWidget()]++;
if (!compositor) {
compositor = new ui::Compositor(
context_factory- >AllocateFrameSinkId(), context_factory,
if (!compositor_) {
return; // already released
}
#else
if (!browser_impl_) {
return;
}
auto it = accelerate_widget_map_.find(browser_impl_- >GetAcceleratedWidget());
if (it == accelerate_widget_map_.end()) {
return;
}
#endif
// Marking the DelegatedFrameHost as removed from the window hierarchy is
// necessary to remove all connections to its old ui::Compositor.
if (delegated_frame_host_) {
if (is_showing_) {
delegated_frame_host_- >WasHidden(content::DelegatedFrameHost::HiddenCause::kOther);
}
delegated_frame_host_- >DetachFromCompositor();
delegated_frame_host_.reset(nullptr);
}
#ifdef DISABLE_GPU
compositor_.reset(nullptr);
#else
LOG(INFO) < < "ReleaseCompositor";
if(--accelerate_widget_map_[browser_impl_- >GetAcceleratedWidget()] == 0) {
if (!browser_impl_) {
return;
}
LOG(INFO) < < "ReleaseCompositor, widget = " < < static_cast< uint32_t >(browser_impl_- >GetAcceleratedWidget());
auto it = compositor_map_.find(browser_impl_- >GetAcceleratedWidget());
if (it != compositor_map_.end()) {
if (it- >second != nullptr) {
delete it- >second;
}
compositor_map_.erase(it);
}
accelerate_widget_map_.erase(browser_impl_- >GetAcceleratedWidget());
}
#endif
}
在 RK3568 上使用 etsWeb 时,使用下列命令可以看到浏览器的进程号 pid 和进程所占内存情况。
ps -ef #查看pid
hidumper --mem 1021 #查看进程所占内存,这里的1021是进程号pid的示例,具体进程号请使用前面提到的命令查看
可以看出浏览器进程所占用的内存会随着网页数量增对而增加,进程在占用内存达到 300M 至 400M 时会崩溃。在增大 basewebwebviewbundle.json 中的 ram 值后,最大占用内存可达到 600M 至 700M,此时浏览器虽然不会崩溃,但是整机会变得非常卡,已经无法使用。研究过相关代码后发现,目前 webview 尚无自动回收资源的接口,这就导致浏览器的网页占用的资源得不到有效的释放,进而导致浏览器进程占用的内存不断增加,最终崩溃。
具体原因是:
1.web 组件销毁时,增加的内存无法被回收,arkui web 组件侧因为强指针相互引用,导致析构函数没有被调用。
2.web 内核侧的 OnDestory 接口没有被调用,内核侧资源没有被回收。
3.web 内核侧 GPU 渲染,new 出来的 compositor 没有被 delete。
经过研究,webview 的内存资源得不到释放的情况系版本 bug,并且这个问题在最新的 master 版本和 3.2beta2 版本已修复。
1.使用智能指针 shared_ptr 时要注意不可以互相引用,否则会导致引用数量无法清零导致内存泄漏;
2.增大系统预设 ram 可以使某些占用内存大的 APP 使用情况得到改善,但从系统总体来看,这种方法存在后患,可能会引发意想不到的问题。
全部0条评论
快来发表一下你的评论吧 !