调用 XmlSerializer.Deserialize() 时出现 OutOfMemoryError - 与 XML 大小无关!

发布于 2024-09-01 12:15:49 字数 657 浏览 4 评论 0原文

这是一个非常疯狂的错误。对于非常短且简单的 XML 片段(例如,),以下内容将引发 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.
    }
}

我在 < a href="http://msdn.microsoft.com/en-us/magazine/cc163491.aspx#S5" rel="nofollow noreferrer">这篇 MSDN 文章 ,如果我在构造函数中,我最终会在每次调用它时生成未缓存的序列化程序集,从而导致程序集泄漏。但我没有在构造函数中使用附加参数。它也会在第一次在新启动的 AppDomain 中调用时发生,因此这也没有意义。

什么给?

This is a really crazy bug. The following is throwing an OutOfMemoryException, for XML snippits that are very short and simple (e.g., <ABC def='123'/>):

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.
    }
}

I read in this MSDN article that if I were using XmlSerializer with additional parameters in the constructor, I'd end up generating un-cached serializer assemblies every time it got called, causing an Assembly Leak. But I'm not using additional parameters in the constructor. It also happens on the first time it is called in a freshly started AppDomain, so that doesn't make sense either.

What gives?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

岁吢 2024-09-08 12:15:49

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

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

根据传闻证据,此问题似乎是由未正确修补的 ODP.NET 程序集版本引起的,该程序集随后通过虚拟机克隆传播到其他系统。

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

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

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

不太漂亮...但这就是解决问题的方法。

Well, the final answer to my question isn't going to help everyone that encounters this, but some of my coworkers also encountered this months later on a different system with a different product. The laughed when they found my post here on SO months later and wondered if I actually had solved it or not, since no solution was accepted here.

The final solution has nothing to do with problems Deserializing. Instead, it involved completely uninstalling and installing a brand new copy of Oracle ODP.NET database client, the provider many if not all of our applications use.

Based on anecdotal evidence, it seems this problem arises on improperly patched versions of ODP.NET assemblies, of which subsequently got propagated to other systems via virtual machine clones.

When ODP.NET was completely removed, and a new compatible version was retrieved from the Oracle website and installed, the problem disappeared completely.

The hypothesis is that a usable (but corrupt) ODP.NET driver has unsafe code and was repeatably overwriting the .NET protected memory area near the Deserialize method after first use. If Deserialize was called before any ODP.NET invocations, it would work just fine. However, all subsequent calls to Deserialize after using any ODP.NET calls would fail miserably.

The final solution to this that has now been resolved twice in two distinct products is to get a good/fresh/clean/new copy of ODP.NET installed.

Not pretty... but that's what solved it.

追我者格杀勿论 2024-09-08 12:15:49

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

相信我找到了这个问题的解决方案。这是 .NET 3.5 SP1

序列化在 3.5 SP1 上使用静态委托和 ISerialized 挂起或引发 OutOfMemoryException(ID:361615):

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

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

解决方案是安装 KB957543 修补程序。

EDIT: This was not the solution, unfortunately, but it may help others track down a very similar problem. This answer here is the actual solution.

I've believe I found the solution to this problem. It is a bug in .NET 3.5 SP1.

Serialization hangs or throws an OutOfMemoryException with static delegate and ISerializable on 3.5 SP1 (ID: 361615):

When a generic class implements ISerializable and has a static delegate member that makes use of the generic type arguments, binary deserialization hangs (on a 32-bit system with Windows Server 2003) or throws an OutOfMemoryException (on a 64-bit system with Windows Server 2008).

This error occurs with .NET 3.5 SP1 and did not occur with .NET 3.5 without SP1.

The solution is to install KB957543 hot fix.

日暮斜阳 2024-09-08 12:15:49

您没有提供足够的详细信息来重现您的问题。但是,阅读器实现了 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);
}

You haven't provided enough details to recreate your problem. But, the reader implements IDisposable and should be disposed of properly. Preferably by wrapping it in a using block. Most developers never run into a problem when they forget to dispose of something because the garbage collector will eventually clean up the mess. However, it isn't hard to code something that causes problems before the GC gets around to cleanup, or even prevents cleanup entirely.

public static T DeserializeXmlNode<T>(XmlNode node)
{
    XmlSerializer xs = new XmlSerializer(typeof(T));
    using(XmlNodeReader xr = new XmlNodeReader(node))
        return (T)xs.Deserialize(xr);
}
绮烟 2024-09-08 12:15:49

基于 XmlSerializer 类的文档您应该缓存 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.
}

Based on the documentation of the XmlSerializer class you should cache the XmlSerializers otherwise you can cause poor performance or a memory leak.

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.
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文