Each time view->page()->setUrl() is called, memory usage increases.
Steps to Reproduce:
- In a loop, open two pages in the same QWebEngineView.
Expected Result:
- No memory leak should occur.
Actual Result:
- Memory usage increases with each iteration.
- On GNU/Linux, the number of child QObjects of QWebEngineView keeps growing (see logs below).
- On macOS, vmmap shows an increasing number of IOSurface objects (see logs below).
Workarounds / Fixes:
macOS:
- set the flag {{qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu-compositing");}}
GNU/Linux:
- set the flags{{ qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu-compositing"); qputenv("QT_QPA_PLATFORM", "vnc");}}
- or delete and recreate the QWebEngineView on each iteration.
Code:
int main(int argc, char *argv[]) { // NOTE: this line fix memory leak on mac os x and windows //qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu-compositing"); // NOTE: this lines fix memory leak on GNU/Linux //qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu-compositing"); //qputenv("QT_QPA_PLATFORM", "vnc"); QApplication a(argc, argv); MainWindow w; w.show(); w.resize(1000, 600); return a.exec(); } ... MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , view_(new QWebEngineView(this)) { view_->page()->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false); setCentralWidget(view_); connect(view_, &QWebEngineView::loadFinished, this, &MainWindow::onLoadFinished, Qt::UniqueConnection); connect(view_, &QWebEngineView::renderProcessTerminated, this, [](QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode) { qDebug() << "TERM: status =" << terminationStatus << "code =" << exitCode; }); QTimer::singleShot(50, this, [this]() { view_->page()->setUrl(QUrl("https://google.com/")); }); } MainWindow::~MainWindow() { } void MainWindow::onLoadFinished() { static int n = 0; static int prev = 0; const int obj_count = view_->findChildren<QObject *>().count(); const int obj_diff = obj_count - prev; qDebug() << "ITER: N =" << ++n << "obj =" << obj_count << "obj_diff =" << obj_diff; prev = obj_count; QTimer::singleShot(50, this, [this]() { // NOTE: this lines fix memory leak on GNU/Linux //delete view_; //view_ = new QWebEngineView(this); //setCentralWidget(view_); //view_->page()->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false); //connect(view_, &QWebEngineView::loadFinished, this, &MainWindow::onLoadFinished, Qt::UniqueConnection); //connect(view_, &QWebEngineView::renderProcessTerminated, this, [](QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode) { // qDebug() << "TERM: status =" << terminationStatus << "code =" << exitCode; //}); // NOTE: has no effect //delete m_view->page(); //m_view->setPage(new QWebEnginePage(m_view)); if (n % 2) view_->page()->setUrl(QUrl("https://yahoo.com/")); else view_->page()->setUrl(QUrl("https://google.com/")); }); }
GNU/Linux debut output:
ITER: N = 1 obj = 386 obj_diff = 386 ITER: N = 2 obj = 5581 obj_diff = 5195 ITER: N = 3 obj = 5864 obj_diff = 283 ITER: N = 4 obj = 10827 obj_diff = 4963 ITER: N = 5 obj = 11106 obj_diff = 279 ITER: N = 6 obj = 16068 obj_diff = 4962 ITER: N = 7 obj = 16354 obj_diff = 286 ITER: N = 8 obj = 21318 obj_diff = 4964 ITER: N = 9 obj = 21603 obj_diff = 285 ITER: N = 10 obj = 26564 obj_diff = 4961 ITER: N = 11 obj = 26849 obj_diff = 285 ITER: N = 12 obj = 31812 obj_diff = 4963 ITER: N = 13 obj = 32043 obj_diff = 231 ITER: N = 14 obj = 37060 obj_diff = 5017 ITER: N = 15 obj = 37346 obj_diff = 286 ITER: N = 16 obj = 42387 obj_diff = 5041 ITER: N = 17 obj = 42672 obj_diff = 285 ITER: N = 18 obj = 47713 obj_diff = 5041 ITER: N = 19 obj = 47999 obj_diff = 286 ITER: N = 20 obj = 50037 obj_diff = 2038 ITER: N = 21 obj = 53247 obj_diff = 3210 ITER: N = 22 obj = 58208 obj_diff = 4961 ITER: N = 23 obj = 58481 obj_diff = 273 ... ITER: N = 1004 obj = 473936 obj_diff = 171 ITER: N = 1005 obj = 474711 obj_diff = 775 ITER: N = 1006 obj = 475556 obj_diff = 845 ITER: N = 1007 obj = 475657 obj_diff = 101 ITER: N = 1008 obj = 476174 obj_diff = 517 ITER: N = 1009 obj = 476606 obj_diff = 432 ITER: N = 1010 obj = 477127 obj_diff = 521 ITER: N = 1011 obj = 477559 obj_diff = 432 ITER: N = 1012 obj = 477692 obj_diff = 133 ITER: N = 1013 obj = 478503 obj_diff = 811 ITER: N = 1014 obj = 479022 obj_diff = 519 ITER: N = 1015 obj = 479454 obj_diff = 432 ITER: N = 1016 obj = 480301 obj_diff = 847 ITER: N = 1017 obj = 480400 obj_diff = 99 ITER: N = 1018 obj = 480571 obj_diff = 171 ... ITER: N = 1026 obj = 484744 obj_diff = 519 ITER: N = 1027 obj = 485176 obj_diff = 432 ITER: N = 1028 obj = 485627 obj_diff = 451 ITER: N = 1029 obj = 486129 obj_diff = 502 ITER: N = 1030 obj = 486646 obj_diff = 517 ITER: N = 1031 obj = 487078 obj_diff = 432 ITER: N = 1032 obj = 487597 obj_diff = 519 ITER: N = 1033 obj = 488029 obj_diff = 432 ... ITER: N = 2987 obj = 1421279 obj_diff = 101 ITER: N = 2988 obj = 1421796 obj_diff = 517 ITER: N = 2989 obj = 1422225 obj_diff = 429 ITER: N = 2990 obj = 1423071 obj_diff = 846 ITER: N = 2991 obj = 1423172 obj_diff = 101 ITER: N = 2992 obj = 1424015 obj_diff = 843 ITER: N = 2993 obj = 1424114 obj_diff = 99 ITER: N = 2994 obj = 1424637 obj_diff = 523 ITER: N = 2995 obj = 1425068 obj_diff = 431 ITER: N = 2996 obj = 1425600 obj_diff = 532 ITER: N = 2997 obj = 1426029 obj_diff = 429 ITER: N = 2998 obj = 1426550 obj_diff = 521 ITER: N = 2999 obj = 1426979 obj_diff = 429 ITER: N = 3000 obj = 1427513 obj_diff = 534
MacOS X debut output:
... many lines ... IOSurface 37c264000-37cba0000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x1f8 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 37cba0000-37d4dc000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x1f3 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 37d4dc000-37de18000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x239 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 37de18000-37e754000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...D: 0x21 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 37e754000-37f090000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x227 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 37f090000-37f9cc000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...D: 0x26 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 37f9cc000-380308000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x226 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 380308000-380c44000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x258 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 380c44000-381580000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x220 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 381580000-381ebc000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x25a 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 381f20000-38285c000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x23e 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 38285c000-383198000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x242 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 383198000-383ad4000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x232 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 383ad4000-384410000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x22f 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 384410000-384d4c000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x240 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 384d4c000-385688000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x1e7 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 385688000-385fc4000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x229 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 385fc4000-386900000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x23b 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 386900000-38723c000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x247 2000x1200 (BGRA) 9450K 'QtWebEngineCore' IOSurface 38723c000-387b78000 [ 9456K 9456K 9456K 0K] rw-/rw- SM=SHM PURGE=N ...: 0x23d 2000x1200 (BGRA) 9450K 'QtWebEngineCore' ... ... ...==== Legend SM=sharing mode: COW=copy_on_write PRV=private NUL=empty ALI=aliased SHM=shared ZER=zero_filled S/A=shared_alias PURGE=purgeable mode: V=volatile N=nonvolatile E=empty otherwise is unpurgeable==== Summary for process 16976 ReadOnly portion of Libraries: Total=1.9G resident=445.8M(23%) swapped_out_or_unallocated=1.4G(77%) Writable regions: Total=4.0G written=75.5M(2%) resident=2.0G(49%) swapped_out=1.2G(29%) unallocated=893.9M(22%) VIRTUAL RESIDENT DIRTY SWAPPED VOLATILE NONVOL EMPTY REGION REGION TYPE SIZE SIZE SIZE SIZE SIZE SIZE SIZE COUNT (non-coalesced) =========== ======= ======== ===== ======= ======== ====== ===== ======= Accelerate framework 128K 128K 0K 128K 0K 128K 0K 1 Activity Tracing 256K 32K 32K 0K 0K 32K 0K 1 CG image 96K 48K 48K 48K 0K 0K 0K 4 ColorSync 544K 32K 32K 512K 0K 0K 0K 28 CoreAnimation 384K 176K 32K 256K 0K 160K 0K 20 CoreGraphics 32K 32K 32K 0K 0K 0K 0K 2 CoreUI image data 1184K 0K 0K 1184K 0K 0K 0K 9 Foundation 16K 16K 16K 0K 0K 0K 0K 1 IOAccelerator 160K 160K 160K 0K 0K 0K 0K 4 IOAccelerator (graphics) 60.4M 52.3M 51.8M 528K 0K 52.3M 5536K 264 IOKit 160K 80K 80K 0K 0K 16K 0K 10 IOSurface 1.9G 1.9G 744.1M 1.1G 0K 1.9G 0K 219 Kernel Alloc Once 32K 16K 16K 0K 0K 0K 0K 1 MALLOC guard page 288K 0K 0K 0K 0K 0K 0K 18 MALLOC metadata 464K 304K 304K 160K 0K 0K 0K 20 MALLOC_LARGE (empty) 9376K 9376K 9376K 0K 0K 0K 0K 1 see MALLOC ZONE table below MALLOC_LARGE metadata 16K 16K 16K 0K 0K 0K 0K 1 see MALLOC ZONE table below MALLOC_MEDIUM 1.1G 7152K 6224K 1520K 0K 0K 0K 9 see MALLOC ZONE table below MALLOC_MEDIUM (empty) 128.0M 128K 128K 208K 0K 0K 0K 1 see MALLOC ZONE table below MALLOC_NANO 512.0M 8496K 8480K 368K 0K 0K 0K 1 see MALLOC ZONE table below MALLOC_SMALL 88.0M 18.6M 18.6M 2512K 0K 0K 0K 11 see MALLOC ZONE table below MALLOC_TINY 19.0M 7696K 7696K 784K 0K 0K 0K 19 see MALLOC ZONE table below MALLOC_TINY (empty) 8192K 192K 192K 96K 0K 0K 0K 8 see MALLOC ZONE table below Mach message 96K 96K 96K 0K 0K 0K 0K 5 PROTECTED_MEMORY 16K 0K 0K 0K 0K 0K 0K 1 STACK GUARD 56.4M 0K 0K 0K 0K 0K 0K 27 Stack 186.5M 752K 656K 144K 0K 0K 0K 28 Stack (reserved) 544K 0K 0K 0K 0K 0K 0K 1 reserved VM address space (unallocated) Stack Guard 16K 0K 0K 0K 0K 0K 0K 1 VM_ALLOCATE 320K 48K 48K 32K 0K 0K 0K 5 __AUTH 5433K 2529K 532K 49K 0K 0K 0K 688 __AUTH_CONST 76.4M 28.4M 80K 16K 0K 0K 0K 931 __CTF 824 824 0K 0K 0K 0K 0K 1 __DATA 28.4M 10.5M 2269K 890K 0K 0K 0K 935 __DATA_CONST 51.8M 24.6M 96K 0K 0K 0K 0K 961 __DATA_DIRTY 2768K 1714K 658K 47K 0K 0K 0K 339 __FONT_DATA 2352 0K 0K 0K 0K 0K 0K 1 __INFO_FILTER 8 8 0K 0K 0K 0K 0K 1 __LINKEDIT 628.4M 21.7M 0K 0K 0K 0K 0K 24 __OBJC_RO 61.4M 38.3M 0K 0K 0K 0K 0K 1 __OBJC_RW 2396K 1964K 44K 0K 0K 0K 0K 1 __TEXT 1.2G 424.2M 0K 0K 0K 0K 0K 981 __TPRO_CONST 128K 96K 96K 0K 0K 0K 0K 2 mapped file 243.6M 17.3M 0K 0K 0K 0K 0K 40 owned unmapped memory 19.4M 0K 0K 16.4M 0K 0K 0K 1 page table in kernel 964K 964K 964K 0K 0K 0K 0K 1 shared memory 10.6M 1312K 1312K 192K 0K 0K 0K 65 unused but dirty shlib __DATA 343K 209K 209K 134K 0K 0K 0K 301 =========== ======= ======== ===== ======= ======== ====== ===== ======= TOTAL 6.4G 2.5G 853.5M 1.2G 0K 1.9G 5536K 5995 TOTAL, minus reserved VM space 6.4G 2.5G 853.5M 1.2G 0K 1.9G 5536K 5995 VIRTUAL RESIDENT DIRTY SWAPPED ALLOCATION BYTES DIRTY+SWAP REGION MALLOC ZONE SIZE SIZE SIZE SIZE COUNT ALLOCATED FRAG SIZE % FRAG COUNT =========== ======= ========= ========= ========= ========= ========= ========= ====== ====== MallocHelperZone_0x100b40000 1.3G 33.0M 32.1M 5008K 16625 29.3M 7882K 21% 35 DefaultMallocZone_0x100b78000 512.0M 8496K 8480K 368K 110813 7211K 1637K 19% 1 QuartzCore_0x100ed0000 18.0M 400K 400K 16K 1360 97K 319K 77% 11 LSBindingEvaluator_0x1019a4000 3072K 0K 0K 96K 0 0K 96K 100% 3 DefaultPurgeableMallocZone_0x102ec8000 0K 0K 0K 0K 0 0K 0K 0% 0 =========== ======= ========= ========= ========= ========= ========= ========= ====== ====== TOTAL 1.9G 41.7M 40.8M 5488K 128798 36.5M 9934K 22% 50
Full minimal example: