程序编译运行过程很顺利,测试的时候也没发现什么问题。但后来我随手上传了一个1G大小的文件,发现每次文件上传到70%左右的时候程序就崩溃了,小文件就没这个问题。急忙打开任务管理器,这才发现上传文件的时候,程序内存占用会随着上传进度的增加而增加,上传1G文件的时候内存最多会吃到1.5G,这时候程序申请不到更多内存了,我又没做检查,当然就会崩溃掉。

限制上传文件大小这种事我是不会做的,毕竟一个上传工具占用内存比PS都高实在不科学。注意到文件上传完成之后内存会立即回到正常值,显然原因并不是我忘记释放内存而是内存释放不及时,这样看来唯一可疑的地方就是上面chunkUpload函数里面的reply->deleteLater()那一句了吧。于是我写了个方法监听reply的销毁时机,果然每一块上传完成之后reply没有销毁,直到文件全部上传完毕之后才输出一大堆“I’m destroyed…”的信息。

根据Qt文档的说明,QObject::deleteLater()并没有将对象立即销毁,而是向主消息循环发送了一个event,下一次主消息循环收到这个event之后才会销毁对象。我在这里使用deleteLater只是因为Qt文档里推荐这么做而已,其他并没多想。是这样的话一切都说得通了,因为chunkUpload函数是在一个while循环里,程序还没来得及处理这个event就立即进行下一块传输了,传输过程中生成的QNetworkReply以及它关联的QBufferQHttpMultiPart当然也就来不及删除了。崩溃原因找到了。你不就是来不及处理销毁对象的event嘛,手动让你处理下不就行了?于是修改upload函数代码如下:

1
2
3
4
5
6
7
void upload()
{
while (NOT_FINISHED) {
chunkUpload(item, CHUNK_SIZE);
qApp->processEvents(); // 让主程序把消息队列中的QEvent处理完
}
}

编译、运行,内存占用依然没有改变,看样子加这一行用处不大。再次查询QObject::deleteLater()的文档,发现这样一句话:

…for the object to be deleted, the control must return to the event loop from which deleteLater() was called.

这么说来,deleteLater销毁QObject的唯一时机就是程序返回主消息循环以后了呢。无奈只能放弃deleteLater。考虑到每次return前都要手动delete亦或是使用goto语句实在都不够优雅,所以利用Qt自带的QScopedPointer,修改chunkUpload函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int chunkUpload(UploadItem *item, const qint64 &chunkSize)
{
Q_ASSERT(item != 0); // ... QNetworkRequest request;
QScopedPointer<QNetworkReply> reply;
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QBuffer* buffer = new QBuffer(multiPart);
QEventLoop loop;
QTimer timer; // ... append fields to multiPart ... request.setUrl(UPLOAD_URL);
reply.reset(networkManager->post(request, multiPart));
multiPart->setParent(reply.data()); // ... event loop ... if (reply->isRunning())
reply->abort(); if (reply->error() != QNetworkReply::NoError)
return -1; // ... deal with the reply ... return 0;
}

问题解决。

https://blog.yeatse.com/2015/03/18/qobject-deletelater-usage/

QObject::deleteLater()并没有将对象立即销毁,而是向主消息循环发送了一个event,下一次主消息循环收到这个event之后才会销毁对象 good的更多相关文章

  1. Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和。先处理自己队列中的消息,然后再处理系统消息队列中的消息)

    Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...

  2. 一步步搭建自己的轻量级MVCphp框架-(四)一个国产轻量级框架Amysql源码分析(3) 总进程对象

    AmysqlProcess类,框架的总进程对象 ./Amysql/Amysql.php 下面还是和以前一样,先上代码~ class AmysqlProcess { public $AmysqlCont ...

  3. 使用jQuery匹配文档中所有的li元素,返回一个jQuery对象,然后通过数组下标的方式读取jQuery集合中第1个DOM元素,此时返回的是DOM对象,然后调用DOM属性innerHTML,读取该元素 包含的文本信息

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. python在交互模式下直接输入对象后回车,调用的是对象的__repr__()方法,这个方法表示的是一个编码,用print+对象是调用对象的__str__方法

    交互模式下调用对象的__repr__()方法,这个方法表示的是一个编码 >>> u"国庆节快乐"u'\u56fd\u5e86\u8282\u5feb\u4e50' ...

  5. JS对象 Array 数组对象 数组对象是一个对象的集合,里边的对象可以是不同类型的。数组的每一个成员对象都有一个“下标”,用来表示它在数组中的位置,是从零开始的

    Array 数组对象 数组对象是一个对象的集合,里边的对象可以是不同类型的.数组的每一个成员对象都有一个"下标",用来表示它在数组中的位置,是从零开始的 数组定义的方法: 1. 定 ...

  6. C# 基础才是重中之重~对象的生与死

    为何要写 之所以写这篇文章,完全是因为学生们在实际开发中遇到的问题,一个对象占用的内存空间总不被释放,导致系统内存不断攀升,其最主要原因是我们对“对象的生与死”不清楚,或者从来没有认真去考虑过这件事, ...

  7. 【一个小功能】从js判断ie版本,浅谈navigator对象的appName属性

    判断IE版本主要的是获取两个属性,a.当前浏览器名称,b.当前浏览器版本,为此不得不了解navigator对象. 先贴代码 window.onload = function() { var brows ...

  8. 使用final关键字修饰一个引用类型变量时,是引用不能变,还是引用的对象不能变?

    使用final关键字修饰一个引用类型变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的. 测试代码如下: package reviewTest; /** * @ClassName: ...

  9. 什么是不可变对象(immutable object)?Java 中怎么 创建一个不可变对象?

    不可变对象指对象一旦被创建,状态就不能再改变.任何修改都会创建一个新的对象,如 String.Integer 及其它包装类. 详情参见答案,一步一步指导你在 Java中创建一个不可变的类.

随机推荐

  1. node相关--WebSocket

    socket:socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. ajax: 通过HTTP请求+响应模式的方式来发送和 ...

  2. Color a Tree[HDU1055]

    Color a Tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tota ...

  3. 【wikioi】1281 Xn数列(矩阵乘法)

    http://wikioi.com/problem/1281/ 矩阵真是个神奇的东西.. 只要搞出一个矩阵乘法,那么递推式可以完美的用上快速幂,然后使复杂度降到log 真是神奇. 在本题中,应该很快能 ...

  4. C#的async和await

    C# 5.0中引入了async 和 await.这两个关键字可以让你更方便的写出异步代码. 看个例子: public class MyClass { public MyClass() { Displa ...

  5. ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

    ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) ERROR 2013 (HY00 ...

  6. 如何获取checkboxlist的多个选中项

    string[] array = dt.Rows[0]["s_type"].ToString().Split('|');                foreach (ListI ...

  7. tomcat配置虚拟目录映射

    本文主要介绍web虚拟目录映射的两种常用方法及配置默认web应用的方法 一.在Server.xml中进行配置 在<Host>元素中添加子元素<Context path=" ...

  8. JBPM4.4学习API

    一.流程引擎API org.jbpm.api.ProcessEngine是jbpm4所有的Service API 之源. 既所有的Service API(服务接口)都从ProcessEngine中获取 ...

  9. shell 循环

    for循环: 批量删除.gz结尾的文件: 循环打包文件并备份到一个目录下面: find ./ -maxdepth 1 -name "*.gz" find ./ -maxdepth ...

  10. 微博java SDK介绍及使用说明

    转自:作者:新浪微博 开放平台 @MUNTO_AKIRA http://open.weibo.com/blog/%E5%BE%AE%E5%8D%9Ajava-sdk%E4%BB%8B%E7%BB%8D ...