【问题标题】:QML Memory leak sending XMLHttpRequestQML 内存泄漏发送 XMLHttpRequest
【发布时间】:2020-08-06 06:09:52
【问题描述】:

创建XMLHttpRequestnew 实例并调用send() 会导致垃圾收集器或gc() 无法清除内存使用情况。在对象上调用delete 也不会清除内存。

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 640
    height: 480

    Component.onCompleted: {
        for(var i = 0; i < 100000; i++) {
            console.log("Send request " + i)

            var xhttp = new XMLHttpRequest
            xhttp.open('get', 'someurl')
            xhttp.send()
            delete xhttp
        }

        gc() //why won't this clean the instances of XMLHttpRequest???
    }
}

如果我从不打电话给xhttp.send(),那么我就没有任何内存泄漏。垃圾收集启动,因为没有引用 var xhttp 并且内存被释放。我想也许垃圾收集器没有触发,但gc() 也不会清除内存。

这个 MRE 将运行 100,000 次迭代并在内存中保存大约 500MB。这可以通过更改为i &lt; 1000000 轻松容纳 5.0GB。

如何修复此内存泄漏并释放内存?

类似问题的参考: QTBUG-43005 (No resolution) QTBUG-50231 (No resolution)

现在记录在QTBUG-83857

在这里,拥有 2.0GB 的内存。它保持了将近 4 个小时,直到我终止了该程序。当我关闭应用程序时,大约 60 秒后,整个 2GB 内存被释放

尝试使用 QNetworkAccessManager 类

//main.qml
    MyClass {
        id: myNetworkClass

        Component.onCompleted: {
            for(var i = 0; i < 10000; i++) {
                myNetworkClass.doDownload()
            }
        }
    }
//myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDebug>


class MyClass : public QObject
{
    Q_OBJECT

public:
    explicit MyClass(QObject *parent = 0):QObject(){
        manager = new QNetworkAccessManager(this);

        connect(manager, SIGNAL(finished(QNetworkReply*)),
                this, SLOT(replyFinished(QNetworkReply*)));
    }

public:
    Q_INVOKABLE void doDownload() {
        manager->get(QNetworkRequest(QUrl("https://esi.evetech.net/latest/characters/93610700")));
    }

public slots:
    void replyFinished(QNetworkReply *reply) {};

private:
    QNetworkAccessManager *manager;
    int count = 0;
};

#endif // MYCLASS_H

不幸的是,根据 htop,这也保留了内存并且不会释放它。

【问题讨论】:

    标签: javascript qt memory memory-leaks qml


    【解决方案1】:

    编辑

    在 C++ 中创建一个新的控制类来处理您的 Web 请求,然后设置根上下文属性或在 QML 中注册类型并改用 C++ api。

    这是最简单的方法:

    在 myclass.cpp 中,您使用这种类型的代码以及适当的处理程序(replyFinished)创建一个方法

    确保要从 QML 调用的方法在头文件中以 Q_INVOKABLE 为前缀(在 void 之前)

    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    connect(manager, &QNetworkAccessManager::finished,
            this, &MyClass::replyFinished);
    
    manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
    

    --

    现在您只需注册类型并在 main.cpp 中创建一个实例

     qmlRegisterType<MyClass>("MyClass", 1,0, "MyClass");
    

    --

    在你的 QML 文件中做

    import MyClass 1.0
    Window {
       MyClass { 
         id: myNetworkClass
       }
    
      function doLookup() { myNetworkClass.myCustomMethod(); }
    }
    

    这将为您提供一种稳定的方法来避免由于在强类型框架上运行异步且弱类型的原生 javascript 环境而产生的 QML 问题...

    祝你好运!

    原创

    首先,您通过创建请求而不保存对创建的对象的引用来留下悬挂的对象引用...

    将所有 XMLHttpRequest 对象的数组保存在 Window 的属性中

    Window
    {
        property var requests: []
    // ...
    
        Timer {
           onTriggered: { 
     //  add request to array
                 requests.push(xhttp);
           }
        }
    }
    

    然后可能开始删除数组中的对象 ...

    var xhttp = requests.unshift() 
    xhttp.destroy()
    

    但真正的问题是每隔 50 毫秒发送一次 http 请求

    嗯,每秒 20 个或 1200 个/分钟的请求

    您可能需要调整代码以在前一个完成后发送请求.. 或将间隔设置为更高的值

    【讨论】:

    • 谢谢 mike,我继续删除了上面 MRE 中的 Timer{},因为它也可以只用一个 for 循环来完成,我认为它更有意义。不过,我尝试存储到数组并从数组中删除,但没有任何区别。与每次迭代后仅在对象上调用 delete 类似的结果...
    • 这很奇怪,也许您应该使用稳定的 QNetwork API 在 C++ 中实现请求,并在 qml 中将父控制器注册为新的对象类型... 控制这些引用可能会更好.. .
    • 感谢您的编辑,迈克。我使用QNetworkAccessManager 进行了尝试,但我仍然得到了类似的结果......也许有一种正确的方法可以在 c++ 中实现它,但我没有做到这一点?我希望如此,否则我开始认为 QML 并不适用于任何类型的网络请求。我更新了我的问题以反映我的尝试。
    • 好吧,创建网络请求有开销,并且您正在创建大量必须执行请求并缓存响应的对象......我猜这种行为本身就是问题源于..尝试将下一个请求放在replyFinished公共槽中,而不是一次将所有请求都塞进去,一次只做一个..这可能会起作用
    • 我认为 Qt 会创建一个对任何创建异步 Web 请求的对象的新引用,直到它们完成。我的猜测是,这 10,000 个请求中的大多数由于防火墙、节流和滞后而没有完成。除非您手动断开它们,否则它们会被 replyFinished 插槽引用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-06
    • 2016-08-24
    • 2011-11-20
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多