【问题标题】:OutOfMemoryError calling XmlSerializer.Deserialize() - not related to XML size!OutOfMemoryError 调用 XmlSerializer.Deserialize() - 与 XML 大小无关!
【发布时间】:2011-02-18 20:59:13
【问题描述】:

这是一个非常疯狂的错误。对于非常简短的 XML 片段(例如,<ABC def='123'/>),以下是抛出一个 OutOfMemoryException

public static T DeserializeXmlNode<T>(XmlNode node)
{
    try
    {
        return (T)new XmlSerializer(typeof(T))
            .Deserialize(new XmlNodeReader(node));
    }
    catch (Exception ex)
    {
        throw; // just for catching a breakpoint.
    }
}

我在this MSDN article 中读到,如果我在构造函数中使用带有附加参数的 XmlSerializer,我最终会在每次调用它时生成未缓存的序列化程序程序集,从而导致 Assembly Leak .但我没有在构造函数中使用额外的参数。它也发生在第一次在新启动的 AppDomain 中调用时,所以这也没有意义。

什么给了?

【问题讨论】:

  • 最有可能的候选者似乎是您要反序列化的类型 - 您可以发布破坏它的类型的代码吗?
  • 我使用 XSD 架构文档中的 xsd.exe 生成了类。
  • XML 可能有问题。也许某种递归正在进行?产生异常的类型有什么特别之处?我的经验是反序列化的内存不足与内存碎片有关。你有简介吗?一个简单的测试项目会产生同样的异常吗?
  • 你的类有一个属性调用自己吗?这会导致递归,我之前遇到过这个问题。 (但是,内存不足的例外情况除外。)
  • @Mike Atlas 在 XSD 和 XML 中寻找任何奇怪的东西。如果没有更多信息,我认为我们无法做更多事情。如果可能,也发布堆栈。

标签: .net serialization out-of-memory xmlserializer


【解决方案1】:

好吧,我的问题的最终答案不会对遇到此问题的每个人都有帮助,但我的一些同事在几个月后也遇到了使用不同产品的不同系统。几个月后,当他们在这里找到我的帖子并想知道我是否真的解决了它时,他们笑了,因为这里没有接受任何解决方案。

最终解决方案与反序列化问题无关。相反,它涉及完全卸载和安装 Oracle ODP.NET 数据库客户端的全新副本,我们的许多应用程序都使用该提供程序。

根据轶事证据,这个问题似乎出现在未正确修补的 ODP.NET 程序集版本上,这些程序集随后通过虚拟机克隆传播到其他系统。

当ODP.NET被彻底删除,从Oracle网站检索新的兼容版本并安装后,问题就完全消失了。

假设是可用(但已损坏)的 ODP.NET 驱动程序具有不安全的代码,并且在首次使用后重复覆盖 Deserialize 方法附近的 .NET 受保护内存区域。如果在任何 ODP.NET 调用之前调用了 Deserialize,它就可以正常工作。但是,在使用任何 ODP.NET 调用之后对 Deserialize 的所有后续调用都将失败。

在两个不同的产品中已经两次解决了这个问题的最终解决方案是安装一个良好/新鲜/干净/新的 ODP.NET 副本。

不漂亮......但这就是解决它的方法。

【讨论】:

    【解决方案2】:

    编辑:不幸的是,这不是解决方案,但它可以帮助其他人找到一个非常相似的问题。这个答案here 是实际的解决方案。

    相信我找到了解决这个问题的方法。这是.NET 3.5 SP1 中的一个错误。

    在 3.5 SP1 上,序列化挂起或抛出带有静态委托和 ISerializable 的 OutOfMemoryException(ID:361615):

    当泛型类实现 ISerializable 并具有使用泛型类型参数的静态委托成员时,二进制反序列化挂起(在 Windows Server 2003 的 32 位系统上)或抛出 OutOfMemoryException(在 64位系统与 Windows Server 2008)。

    .NET 3.5 SP1 会出现此错误,而没有 SP1 的 .NET 3.5 不会出现此错误。

    解决方法是安装KB957543 hot fix。

    【讨论】:

    • 安装修复程序是否解决了您的问题?因为二进制序列化与 XML 序列化无关,所以我认为这个 KB 不会有帮助。
    • 实际上,在我兴奋中,我刚刚将测试机器回滚到 .NET 3.5 平台,然后 OOM 就如我所愿地消失了。我想我现在需要测试修补程序本身,然后......
    • 是的,实际上这也不是答案。我会把它留在这里一段时间。
    【解决方案3】:

    您没有提供足够的详细信息来重现您的问题。但是,读者实现了 IDisposable 并且应该正确处理。最好将其包装在 using 块中。大多数开发人员在忘记处理某些东西时永远不会遇到问题,因为垃圾收集器最终会清理垃圾。但是,在 GC 开始清理之前编写导致问题的代码并不难,甚至完全阻止清理。

    public static T DeserializeXmlNode<T>(XmlNode node)
    {
        XmlSerializer xs = new XmlSerializer(typeof(T));
        using(XmlNodeReader xr = new XmlNodeReader(node))
            return (T)xs.Deserialize(xr);
    }
    

    【讨论】:

    • 这绝对是我的疏忽,但这真的会在新加载的 AppDomain 中第一次单次调用时耗尽可用内存吗?
    • 不,不会。正如其他 cmets 所建议的那样,我倾向于这是您的目标班级的问题。您可以发布违规课程吗?
    【解决方案4】:

    基于 XmlSerializer 类的 documentation,您应该缓存 XmlSerializer,否则可能导致性能不佳或内存泄漏。

    Hashtable serializers = new Hashtable();
    
    // Use the constructor that takes a type and XmlRootAttribute.
    XmlSerializer s = new XmlSerializer(typeof(MyClass), myRoot);
    
    // Implement a method named GenerateKey that creates unique keys 
    // for each instance of the XmlSerializer. The code should take 
    // into account all parameters passed to the XmlSerializer 
    // constructor.
    object key = GenerateKey(typeof(MyClass), myRoot);
    
    // Check the local cache for a matching serializer.
    XmlSerializer ser = (XmlSerializer)serializers[key];
    if (ser == null) 
    {
        ser = new XmlSerializer(typeof(MyClass), myRoot);
        // Cache the serializer.
        serializers[key] = ser;
    }
    else
    {
        // Use the serializer to serialize, or deserialize.
    }
    

    【讨论】:

    • 我的 OP 中的 MSDN 链接说,使用我正在使用的基本构造函数已经内置缓存。无论如何,我自己第二次缓存它并不能解决我看到的问题,因为它会在第一次使用时立即破坏堆栈 - 那时还没有制作其他序列化程序,但这会填满内存。跨度>
    猜你喜欢
    • 2010-10-05
    • 2015-08-07
    • 1970-01-01
    • 2012-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多