【问题标题】:Memory leak at ProtoBuf serialization on .net core.net 核心上 ProtoBuf 序列化的内存泄漏
【发布时间】:2017-11-30 15:09:59
【问题描述】:

我正在尝试找出我的 .net 核心应用程序与 protobuf 序列化问题的原因,所以我编写了一个测试应用程序并且......有些奇怪。如果我使用 Task 会有内存泄漏,如果我使用 Thread 则没有内存泄漏。

任务示例:

internal class Program
{
    private static void Main(string[] args)
    {
        var data = new Person
        {
            Id = 1,
            Name = new string('q', 10000),
            Address = new Address
            {
                Line1 = new string('w', 10000),
                Line2 = new string('e', 10000)
            }
        };



        //for (var i = 0; i < 100; i++)
        //{
        //    var thread = new Thread(() =>
        //    {
        //        while (true)
        //            try
        //            {
        //                var b = ProtoSerialize(data);
        //                Person serializedPerson;
        //                using (Stream stream2 = new MemoryStream(b))
        //                {
        //                    serializedPerson = Serializer.Deserialize<Person>(stream2);
        //                }
        //            }
        //            catch (Exception e)
        //            {
        //                Console.WriteLine(e);
        //            }
        //    });
        //    thread.Start();
        //}

        for (var i = 0; i < 100; i++)
        {
            new Task(() =>
            {
                while (true)
                {
                    var b = ProtoSerialize(data);
                    Person serializedPerson;
                    using (Stream stream2 = new MemoryStream(b))
                    {
                        serializedPerson = Serializer.Deserialize<Person>(stream2);
                    }
                }
            }).Start();
        }

        Console.ReadLine();
    }


    public static byte[] ProtoSerialize<T>(T record) where T : class
    {
        using (var stream = new MemoryStream())
        {
            Serializer.Serialize(stream, record);
            return stream.ToArray();
        }
    }
}


[ProtoContract]
public class Person
{
    [ProtoMember(1)]
    public int Id { get; set; }

    [ProtoMember(2)]
    public string Name { get; set; }

    [ProtoMember(3)]
    public Address Address { get; set; }
}

[ProtoContract]
public class Address
{
    [ProtoMember(1)]
    public string Line1 { get; set; }

    [ProtoMember(2)]
    public string Line2 { get; set; }
}

和dotMemory的截图:

所以如果我使用线程(注释代码)

            for (var i = 0; i < 100; i++)
        {
            var thread = new Thread(() =>
            {
                while (true)
                    try
                    {
                        var b = ProtoSerialize(data);
                        Person serializedPerson;
                        using (Stream stream2 = new MemoryStream(b))
                        {
                            serializedPerson = Serializer.Deserialize<Person>(stream2);
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                    }
            });
            thread.Start();
        }

那么就没有泄漏:

所以问题是:

  1. 我做错了吗?

  2. 为什么会这样?

  3. 怎么做?

附言

protobuf-net 是 2.3.2

.net 核心 2.0

UPD1:在主线程中调用 GC.Collect 没有任何改变(仍然存在内存泄漏)

【问题讨论】:

  • 打开快照比较,打开“新”对象,看看这些对象是什么以及它们为什么保存在内存中。使用内存分析器功能。
  • 注意:昨天发布了一个新版本,它解决了一个非常具体的内存场景 - 不确定这是否相关,但您可能想尝试一下
  • 我遇到了完全相同的问题,正在处理大对象并使用 dotmemory 发现是在 Serializer 类上

标签: multithreading memory-leaks asp.net-core protocol-buffers protobuf-net


【解决方案1】:

嗯,这很有趣。 protobuf-net 确实有限使用了一些背景池——例如,ProtoReader 有一个 [ThreadStatic] 实例——尽管它本身就是一个轻对象;虽然它确实保留了一个方便的byte[] 池,但[ThreadStatic] 实例不会引用它(在此之前缓冲区被释放到池中)。所以没有什么立即会浮现在脑海中;您是否尝试过强制GC.Collects 看看是否有任何东西实际上泄漏,而不是根本没有被收集?您是否尝试过用虚拟代码(例如Thread.Sleep(100);)替换所有 protobuf-net 以查看您是否只是看到 TPL 机器的重量?您知道(通过该工具)哪些对象类型导致了开销?

这也有可能只是由于这里使用非常不同的线程使GC 不安:Task 代码将使用ThreadPool,因此线程数量有限。这既不好也不坏 - 只是:不同。

【讨论】:

    猜你喜欢
    • 2011-09-19
    • 2013-07-29
    • 1970-01-01
    • 1970-01-01
    • 2015-11-05
    • 2011-04-02
    • 2011-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多