【问题标题】:EOutOfMemory Creating Large XML Using DelphiEOutOfMemory 使用 Delphi 创建大型 XML
【发布时间】:2009-09-30 18:00:02
【问题描述】:

我正在使用 Delphi 从关系数据库中的数据创建 XML 文档。它在小型数据集上测试良好,但是当我尝试将数据集的大小扩展到生产级别时,它最终会在节点创建期间出现 EOutOfMemory 异常。

我正在使用放置在表单上的 TXMLDocument(MSXML 作为供应商),我的代码通常如下所示:

  DS := GetDS(Conn, 'SELECT Fields. . . FROM Table WHERE InsuredID = ' +IntToStr(AInsuredID));

  try

    while not DS.Eof do
      with ANode.AddChild('LE') do
      begin
        AddChild('LEProvider').Text := DS.FieldByName('LEProvider').AsString;
        // Need to handle "other" here
        AddChild('Date').Text       := DateToXMLDate(DS.FieldByName('LEDate').AsDateTime);
        AddChild('Pct50').Text      := DS.FieldByName('50Percent').AsString;
        AddChild('Pct80').Text      := DS.FieldByName('80Percent').AsString;
        AddChild('Actuarial').Text  := DS.FieldByName('CompleteActuarial').AsString;
        AddChild('Multiplier').Text := DS.FieldByName('MortalityMultiplier').AsString;
        DS.Next;
      end;

  finally

    DS.Free;

  end;

对于这个部分,以及许多其他类似构造的部分,适用于不同的数据库表,执行了很多次。在这个例子中,ANode 是一个 IXMLNode 传递给函数以用作容器。

我预计磁盘上生成的 XML 文件不会超过 10 兆字节。我假设我在创建和处理 XMLNode 时以某种方式泄漏了内存,但我对接口不够熟悉,不知道如何追踪我的问题。

【问题讨论】:

    标签: xml delphi msxml


    【解决方案1】:

    TXMDocument 是一个 DOM 风格的界面,将整个文档保存在内存中。那样内存会很快用完。即使生成的文件不是那么大。您实际上并不需要 TXMLDocument 来写出一个简单的 XML。为什么不直接写入xml格式的文件呢?

    话虽如此:这也可能是由于堆碎片或真正的内存泄漏导致的错误。您可能想尝试这里提到的工具:Profiler and Memory Analysis Tools for Delphi

    【讨论】:

    • 我同意 Lars - 只需使用 writeln() 或直接写入流。它速度更快,使用的内存更少。
    • 谢谢。是 TXMLDocument 还是 MSXML 中的内存限制?在现代世界中,这似乎不是一个合理的限制。如果可能的话,我更喜欢使用能够理解 XML 格式的东西。此外,作为创建过程的最后阶段,我将针对 XSD 验证 XML。
    • 嗯。 Larses 的两个回答,那是我在大学时的昵称。
    • TXMLDocument 是 MSXML 的包装器。所有 DOM API 都需要大量 RAM。我很难说你的问题是由于这个事实还是另一个问题。
    • Lars:我的意思不是(太)密集,但您是说所有面向 DOM 的 XML 库都仅限于可放入内存的 XML 文档,包括开销?看起来我肯定会接受你关于直接文本写入的建议(增加了复杂性)。不幸的是,我也在编写这个 XML 事务的接收端。 . .
    【解决方案2】:

    每个AddChild 调用都将其结果存储到编译器隐式声明的临时IXmlNode 变量中。当当前子例程返回时(无论是正常还是异常),它们应该被自动清理。您可以通过声明自己的变量来使它们的生命周期更加明确。

    var
      le, child: IXmlNode;
    begin
      DS := GetDS(Conn, Format(Query, [AInsuredID]));
      try
        while not DS.Eof do begin
          le := ANode.AddChild('LE');
          child := le.AddChild('LEProvider');
          child.Text := DS.FieldByName('LEProvider').AsString;
          // Need to handle "other" here
          child := le.AddChild('Date');
          child.Text := DateToXMLDate(DS.FieldByName('LEDate').AsDateTime);
          child := le.AddChild('Pct50');
          child.Text := DS.FieldByName('50Percent').AsString;
          child := le.AddChild('Pct80');
          child.Text := DS.FieldByName('80Percent').AsString;
          child := le.AddChild('Actuarial');
          child.Text := DS.FieldByName('CompleteActuarial').AsString;
          child := le.AddChild('Multiplier');
          child.Text := DS.FieldByName('MortalityMultiplier').AsString;
          DS.Next;
        end;
      finally
        DS.Free;
      end;
    end;
    

    在上面的代码中,没有隐式的接口变量。编译器会为每个AddNode 调用声明一个新的隐式变量,但上面的代码表明只有两个是必需的,因为child 可以被每个新的子节点重用。

    不过,仅此代码不应导致过多的内存使用。您似乎更有可能保留对不再需要的对象的引用,或者您正在为某些接口对象创建循环引用。 MSXML 库不应创建自己的任何循环引用,但您尚未显示可能在此处运行的所有代码。

    【讨论】:

    • 假设附加到这些隐式变量的内存将在超出范围时被释放(我不清楚为什么这是在函数的末尾而不是在语句完成处理之后)那么我宁愿避免额外的代码。我确实注意到,在发布我的问题之前研究这个问题时,有人建议 IXMLNodes 实际上可能不会在将来某个不确定的时间放弃最后一个引用时被释放。 MSXML 是否可能有一些内部垃圾收集跟不上我对节点的使用?
    • 编译器的隐式变量和普通声明的变量一样,只是它们没有名字。就像所有其他变量一样,它们在函数末尾超出范围。这样,编译器只需插入一个隐含的“finally”块来清理所有内容。也许您将它们与 C++ 临时文件混为一谈,这些临时文件一直存在到当前语句的末尾。但无论如何,是的,MS XML 保存的时间可能比您需要的时间长。考虑在使用完 TXmlDocument 对象后销毁整个对象,然后在需要时再创建一个新对象。
    • Thx Rob,不幸的是,EOutOfMemory 发生在我构建并保存我的第一个完整文档之前。不与 C++ 混为一谈,只是不知道这些匿名对象是如何被释放的。
    【解决方案3】:

    尝试使用 SAX 解析器而不是 DOM。 DOM 将整个 XML 文件的表示形式保存在内存中。

    试试here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多