简体   繁体   English

QML Memory 泄漏发送 XMLHttpRequest

[英]QML Memory leak sending XMLHttpRequest

Creating new instances of XMLHttpRequest and calling send() results in memory usage that can't be cleared by the garbage collector, nor gc() .创建XMLHttpRequestnew实例并调用send()会导致垃圾收集器无法清除 memory 的使用,也无法清除gc() Calling delete on the object doesnt' clear the memory neither.在 object 上调用delete也不会清除 memory。

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???
    }
}

If I never call xhttp.send() then I don't have any memory leak.如果我从不调用xhttp.send()那么我没有任何 memory 泄漏。 Garbage collection kicks in since there's no reference to the var xhttp and the memory is freed.垃圾收集开始了,因为没有引用var xhttp并且 memory 被释放。 I thought maybe the garbage collector wasn't triggering, but gc() wouldn't clear the memory neither.我想也许垃圾收集器没有触发,但gc()也不会清除 memory 。

This MRE will run 100,000 iterations and holds about 500MB in memory.这个 MRE 将运行 100,000 次迭代,并在 memory 中保存大约 500MB。 This can easily hold 5.0GB by changing to i < 1000000 .这可以通过更改为i < 1000000轻松容纳 5.0GB。

How do I fix this memory leak and free the memory?如何修复此 memory 泄漏并释放 memory?

References to similar questions: QTBUG-43005 (No resolution) QTBUG-50231 (No resolution)类似问题的参考: QTBUG-43005(无解析) QTBUG-50231(无解析)

Now documented on QTBUG-83857现在记录在QTBUG-83857

在此处输入图像描述 Here it is, holding 2.0GB of memory.在这里,持有 2.0GB 的 memory。 It held it for nearly 4 hours until I killed the program.它保持了将近 4 个小时,直到我终止了该程序。 When I closed the application, after about 60 seconds the whole 2GB memory was freed当我关闭应用程序时,大约 60 秒后,整个 2GB memory 被释放

An Attempt at using QNetworkAccessManager class尝试使用 QNetworkAccessManager class

//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

This unfortunately also holds onto memory and doesn't release it, according to htop.根据 htop 的说法,不幸的是,这也保留了 memory 并且没有释放它。

EDIT编辑

Create a new controlling class in C++ to handle your web requests and then set a root context property or register the type in QML and use the C++ api instead. Create a new controlling class in C++ to handle your web requests and then set a root context property or register the type in QML and use the C++ api instead.

Here's the simplest way to go:这是 go 的最简单方法:

in myclass.cpp you create a method with this type of code along with the appropriate handlers (replyFinished)在 myclass.cpp 中,您使用这种类型的代码以及适当的处理程序(replyFinished)创建一个方法

Make sure the method you want to call from QML is prefixed with Q_INVOKABLE in the header file (right before void)确保要从 QML 调用的方法在 header 文件中以 Q_INVOKABLE 为前缀(就在 void 之前)

QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished,
        this, &MyClass::replyFinished);

manager->get(QNetworkRequest(QUrl("http://qt-project.org")));

-- --

Now you simply register the type and create an instance in main.cpp现在您只需注册类型并在 main.cpp 中创建一个实例

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

-- --

and in your QML file do并在您的 QML 文件中执行

import MyClass 1.0
Window {
   MyClass { 
     id: myNetworkClass
   }

  function doLookup() { myNetworkClass.myCustomMethod(); }
}

This will give you a stable way to avoid the QML issues that stem from running a native javascript environment which is asynchronous and weakly typed on a strongly typed framework...这将为您提供一种稳定的方法来避免因运行本机 javascript 环境而导致的 QML 问题,该环境在强类型框架上是异步且弱类型的...

Good luck!祝你好运!

ORIGINAL原来的

First off, you are leaving hanging object references by creating the request without saving a reference to the created object...首先,通过创建请求而不保存对创建的 object 的引用,您将挂起 object 引用...

Keep an array of all XMLHttpRequest objects in a property of Window在 Window 的属性中保留所有 XMLHttpRequest 对象的数组

Window
{
    property var requests: []
// ...

    Timer {
       onTriggered: { 
 //  add request to array
             requests.push(xhttp);
       }
    }
}

Then maybe start deleting the objects in your array with...然后也许开始删除数组中的对象......

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

But the real issue is in sending http requests at 50ms intervals但真正的问题是每隔 50 毫秒发送 http 请求

Thats ummm 20 per second or 1200/minute requests那是 ummm 每秒 20 个或 1200 个/分钟的请求

You might want to adjust the code to send a request once the previous one finishes.. or set the interval to a higher value您可能需要调整代码以在前一个完成后发送请求..或将间隔设置为更高的值

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM