OpenHarmony开发者论坛

标题: webview内存泄漏问题的分析报告 [打印本页]

作者: Laval社区小助手    时间: 2024-1-2 16:35
标题: webview内存泄漏问题的分析报告
[md]# 1 关键字

webview;内存泄漏

# 2 问题描述

问题现象:在3.1release版本和3.2bete1版本中,在RK3568上使用etsWeb和其他浏览器时,webview所占的内存会随着使用而不断增大,最终导致浏览器APP因内存泄漏而崩溃。

# 3 问题原因

## 3.1 正常机制

在任意版本上使用浏览器APP,可以长时间正常浏览网页。

## 3.2 异常机制

在3.1release和3.2beta1上使用浏览器APP,长时间浏览网页后,应用会崩溃。

# 4 解决方案

* arkui web侧,在析构函数中调用OnDestroy方法销毁组件

```
//文件路径foundation\arkui\ace_engine\frameworks\core\components\web\resource\web_delegate.cpp
WebDelegate::~WebDelegate()
{
   ReleasePlatformResource();
   if (nweb_) {
       nweb_->OnDestroy();
   }
}
```

* web内核中的compositor被delete的过程如下

```
//文件路径src\cef\libcef\browser\osr\render_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:elegatedFrameHost::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
}
```

# 5 定位过程

在RK3568上使用etsWeb时,使用下列命令可以看到浏览器的进程号pid和进程所占内存情况。

```
ps -ef #查看pid
hidumper --mem 1021 #查看进程所占内存,这里的1021是进程号pid的示例,具体进程号请使用前面提到的命令查看
```

可以看出浏览器进程所占用的内存会随着网页数量增对而增加,进程在占用内存达到300M至400M时会崩溃。在增大base\\web\\webview\\bundle.json中的ram值后,最大占用内存可达到600M至700M,此时浏览器虽然不会崩溃,但是整机会变得非常卡,已经无法使用。研究过相关代码后发现,目前webview尚无自动回收资源的接口,这就导致浏览器的网页占用的资源得不到有效的释放,进而导致浏览器进程占用的内存不断增加,最终崩溃。

具体原因是:

1. web组件销毁时,增加的内存无法被回收,arkui web组件侧因为强指针相互引用,导致析构函数没有被调用。
2. web内核侧的OnDestory接口没有被调用,内核侧资源没有被回收。
3. web内核侧GPU渲染,new出来的compositor没有被delete。

经过研究,webview的内存资源得不到释放的情况系版本bug,并且这个问题在最新的master版本和3.2beta2版本已修复。

# 6 知识分享

1. 使用智能指针shared\_ptr时要注意不可以互相引用,否则会导致引用数量无法清零导致内存泄漏;
2. 增大系统预设ram可以使某些占用内存大的APP使用情况得到改善,但从系统总体来看,这种方法存在后患,可能会引发意想不到的问题。
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5