代码之家  ›  专栏  ›  技术社区  ›  migajek

Delphi:在类与记录中存储数据,减少内存使用

  •  4
  • migajek  · 技术社区  · 15 年前

    在应用程序工作时,我有很多数据要在内存中存储、读取和修改。可以将数据与树进行比较,在树中,每个节点都由有限数量的字符串和整数描述,并且具有相当多的子元素。 当前数据是使用类/对象存储的,例如

    TRootElement = class
      fName, fDescription: string;
      fPos: integer;
      /// etc
    end;
    
    fDocs: TObjectList; //list of TVariable = class(TRootElement)
    fClasses: TObjectList; // list of TClass=class(TRootElement)
    

    目前程序消耗的内存是不可接受的,因此我正在寻找解决方案来限制它。

    我的问题是: 如果我用基于记录的体系结构替换当前的、面向对象的和基于对象的体系结构,消耗会显著减少吗? 例如,常规记录可以包含:

    TRootElement = record
      fType: TElemType; // enum: root, variable, class, etc ... 
      fName, fDesc: string; 
      // all the fields used by root elem and it's descendants there
    end;
    

    我应该用指向下一个/上一个元素的指针替换tlist吗?因为我从不按索引访问列表的元素,所以我总是循环访问整个列表,这不应该很难做到…不过,如果不必要的话,我想避免。

    谢谢! M

    4 回复  |  直到 10 年前
        1
  •  14
  •   Rob Kennedy    15 年前

    将类更改为记录将减少内存使用,但随着类或记录中字段数的增加,节省的重要性会降低。类和相应记录之间的大小差正好是四个字节,这说明 VMT pointer 一个类持有但没有记录。当考虑权衡时,这种差异通常可以忽略不计:为了节省四个字节,您放弃了继承、多态性、数据隐藏和其他面向对象的特性。(Delphi的新“使用方法的记录”可能会缓解其中的一些问题,但是如果您只有Delphi2005,那么您还没有该功能。)

    实际上,如果这四个字节 真正地 改变你的计划,那么你可能有更大的问题要解决。只需向树中添加另一个节点,就可以消除四字节的节省。有了足够大的数据集,任何一个节点的大小都无关紧要,因为您无论如何都无法将它们保存在内存中。您需要研究某种缓存方案,因此只有一些节点保存在内存中,其余的节点保存在其他地方,例如文件或数据库中。

    如果将当前列表替换为双链接的节点列表,则可能会看到内存使用情况。 增加 因为现在每个节点都在跟踪它的下一个和前一个邻居,而在 TObjectList 是自己管理的。

        2
  •  2
  •   Community CDub    7 年前

    当前程序消耗的内存不可接受

    不可接受的含义是什么?你测量过了吗?事实是什么(对象数量、对象大小、已用内存)?

    你有没有和fastmm检查一下你的程序是否有内存泄漏?如果不是,这是你应该做的第一件事。

    如果您的列表经常增加,那么可能您的内存碎片有问题。使用列表的Capacity属性(如果可能)。在这种情况下,链接列表会有所帮助,但链接列表需要的内存比tlist多(如果容量使用合理)。见 How to monitor or visualize memory fragmentation of a delphi application 有关如何检查的详细信息。

    对于delphi<=2005,用fastmm替换borland内存管理器是很有帮助的,这是非常容易做到的。

    至少,像罗布一样,我认为改变记录并不能解决你的问题。

        3
  •  2
  •   Jeroen Wiert Pluimers    15 年前

    与绝大多数IDE相比,只加载10兆字节的所有php5元数据的内存增加实际上相当不错。

    如果这真的值得你付出努力,我将从字符串文字合并开始。

    使所有字符串都进入全局表(或字典),并从所有字符串中指向它。

    您可以更进一步,因为PHP5语言和库是非常静态的:将整个数据结构从动态转换为静态常量(使用记录)以及所有索引枚举类型。

    您可以做的是将所有字符串设置为resourcestrings或string常量,并查看Delphi编译器是否可以为您进行文本合并。

    我刚刚注意到,您还加载了所有php5内容的文档。这占了相当多的记忆。 您可能需要将这些加载到压缩流中。

        4
  •  1
  •   Marco van de Voort    15 年前

    如果你能像科内尔所说的那样对字符串设置限制,那就真的很重要了。ansistring有一些内部开销,另外还有一些额外开销。但是,即使不使用,也始终会分配短字符串。

    如果您的内存非常紧张,那么为字符串进行自己的分配就更明智了,特别是在数据相对不变的情况下。然后简单地分配一个大的块,并将所有字符串放在其中,前缀为16位左右的长度。

    减少低级技巧,例如简单地消除(某些)字符串也可以节省大量的存储空间。

    注意,rob的record vs class讨论只有在您设法静态地在内存中实例化您所分配的非常便宜的类时才能进行,而您可能不会这样做。这是因为您可以使用记录数组。否则,它始终是引用类型会导致堆开销和-slack(fastmm,16字节粒度)

    我建议不要使用tstringlist/tlist/tobjectlist,因为在非常大的列表(数百万)中插入删除可能很痛苦,因为删除/插入是O(n),而插入中间意味着移动一半的数据。这在20-100K和1M元素之间会很痛苦,这取决于您的访问模式。

    使用一个tlist列表,不要让每个tlist变得太大,这已经是一个很好的解决方法。

    当我这样做时(对于一个2GB服务器内存仍为2000美元的OLAP集群),我甚至在某一点上使用指针中的对齐位来存储分配的大小类。不过,我不建议这样做:—)

    当然,使用fpc进行64位也是一种选择。我已经得到了32位以上解决方案的核心服务器部分,在不到一个小时的时间内以64位的速度工作。