• 可恶的QT隐式共享


    这个问题隐藏的很深,一般不容易察觉它造成的问题,而只是享受它提供的好处(节省内存,而且速度更快)。

    但我发现它现在至少造成两个问题:

    1. 把大量的QString放到QMap里,使用完毕后清空QMap,然而因为隐式共享的原因,实际上QString占用的大量内存得不到释放。这样程序积累了大量无用数据的内存,从此程序运行变得异常缓慢。

    2. QFileInfo也有隐式共享问题,造成读取新文件信息的时候,估计要和旧文件信息全部对比一遍(就算是通过hash对比也很慢啊,我这里测试文件有11万个呢),确定没有这个新文件,然后才开始真正工作。这样效率极低。部分解决办法是使用const解决QFileInfo的问题。但是QString却不能使用const,因为我中间还要修改它。

    只能慢慢研究了~

    官方文档:

    http://doc.qt.io/qt-4.8/implicit-sharing.html

    http://doc.qt.io/qt-5/qstring.html

    中文博客:

    http://blog.csdn.net/yestda/article/details/17893221

    http://blog.chinaunix.net/uid-27177626-id-3949985.html

    http://blog.csdn.net/zhu_xz/article/details/6061201

    ------------------------------------------------------------------

    隐式共享
    说明对象在自操作前,指向一块共享的内存。
    在自操作时,就会发生写时拷贝,让自己指向一块新的内存,这个内存也可以被其他对象引用,所以这个内存块有引用计数。当这块内存的引用计数为零时,会被回收。

    所以,除非
    QString *newString = new QString(theOldQString);
    // then i forget to delete the newString
    就会发生共享内存被引用但是却没被释放的问题。
    其实隐式共享的内存确定可以被释放,但还是应用程序却仍然占据了高内存
    这个就是你想要搞明白的问题

    但是

    for (int i=0; i<5000000; i++) {
    QString str = "b";
    list << str;
    }
    就不存在隐式共享的问题,会把整个对象放在list里

    --你先吧所有的函数参数修改为标准的const引用类型~
    ------------------------------------------------------------------

    for (int i=0; i<5000000; i++) {
    QString str = "aaaa";
    list << str;
    }

    QString str = "aaaa";
    for (int i=0; i<5000000; i++) {
    list << str;
    }
    两段代码会使用完全不同的内存大小

    ------------------------------------------------------------------

    for (int i=0; i<5000000; i++) {
    QString str = "a";
    list << str;
    }

    for (int i=0; i<5000000; i++) {
    QString str = "aaaa";
    list << str;
    }
    两段代码也会使用完全不同的内存大小,而且使用内存都特别大。

    ------------------------------------------------------------------

    理论知识:

    不同于 Java 风格遍历器,STL 风格遍历器直接指向元素本身。容器的begin()函数返回指向该容器第一个元素的遍历器;end()函数返回指向该容器最后一个元素之后的元素的遍历器。end()实际是一个非法位置,永远不可达。这是为跳出循环做的一个虚元素。如果集合是空的,begin()等于end(),我们就不能执行循环。
      由于有隐式数据共享(我们会在后面的章节介绍该部分内容),即使一个函数返回集合中元素的值也不会有很大的代价。Qt API 包含了很多以值的形式返回QList或QStringList的函数(例如QSplitter::sizes())。如果你希望使用 STL 风格的遍历器遍历这样的元素,应该使用容器的拷贝,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 正确的方式
    const QList<QString> sizes = splitter->sizes();
    QList<QString>::const_iterator i;
    for (i = sizes.begin(); i != sizes.end(); ++i)
    ...
     
    // 错误的方式
    QList<QString>::const_iterator i;
    for (i = splitter->sizes().begin();
    i != splitter->sizes().end(); ++i)
    ...

      这个问题不存在于那些返回集合的 const 或非 const 引用的函数。隐式数据共享对 STL 风格遍历器造成的另外影响是,在容器上运行着非 const 遍历器的时候,不能对容器进行拷贝。Java 风格的遍历器没有这个问题。
      foreach关键字
      如果类型名中带有逗号,比如QPair<int, int="">,我们只能像上面一样,先创建一个对象,然后使用foreach关键字。如果没有逗号,则可以直接在foreach关键字中使用新的对象。
      Qt 会在foreach循环时自动拷贝容器。这意味着,如果在遍历时修改集合,对于正在进行的遍历是没有影响的。即使不修改容器,拷贝也是会发生的。但是由于存在隐式数据共享,这种拷贝还是非常迅速的。
      因为foreach创建了集合的拷贝,使用集合的非 const 引用也不能实际修改原始集合,所修改的只是这个拷贝。

    参考:http://jukezhang.com/2014/11/23/learn-qt-eight/
    参考:http://www.devbean.net/2013/01/qt-study-road-2-implicit-sharing/
    http://www.devbean.net/2013/01/qt-study-road-2-iterator/
  • 相关阅读:
    微服务实战——微服务架构选型SpringCloud / Dubbo / K8S比较(一)
    微服务实战——Spring Cloud + Zuul Gateway + Eureka集成
    微服务实战——SpringCloud与Feign集成
    微服务实战——高可用的SpringCloudConfig
    制作自己的网站第二步***在Linux上装上需要的软件以及部署项目配置**
    Eclipse打war包方法以及Eclipse移植项目时JDK版本不匹配Project facet Java version 1.7 is not supported
    Eclipse移植项目时JDK版本不匹配Project facet Java version 1.7 is not supported
    个人网站开发***云服务器+Linux+域名***
    SaaS 系统架构,Spring Boot 动态数据源实现!
    Spring Security 是如何在 Servlet 应用中执行的?
  • 原文地址:https://www.cnblogs.com/findumars/p/4687275.html
Copyright © 2020-2023  润新知