【问题标题】:EF6 Memory leak without any reasonEF6内存泄漏无缘无故
【发布时间】:2017-02-06 17:53:26
【问题描述】:

我发现这个问题已经有一段时间了,但我 100% 确定它正在发生,至少在我的机器上。

我发现只是这一小段代码导致了内存泄漏,但我仍然不知道为什么。

for (int i = 0; i < 1000000; i++)
{
    using (var db = new TestEntity())
    {
        db.Configuration.AutoDetectChangesEnabled = false;
        db.Configuration.ValidateOnSaveEnabled = false;

        // If I just create new Context everything is normal.
        // I need to request anything 
        {
            var any = (bool)db.Test.AsNoTracking().Any();
        }

        // Or more simply just in that way
        // var any = db.Test.AsNoTracking().Any();

    }
    if (i % 1000 == 0)
    {
        Console.WriteLine(i.ToString());
    }
}

这段代码非常简单,但我仍然看不出是什么导致了问题。

我在单次迭代中创建新的上下文,然后只是 Dispose() 它。 我的 bool 变量是本地的,从未使用过。我也在使用Any(),它返回布尔值而不是对对象的引用。

我不同意Garbage Collector 没有时间收集,因为无论我强迫他到哪里Collect() 它仍然在泄漏。 EF 团队还建议尽可能短的使用 Context。

更重要的是,这种泄漏正在/发生在非托管内存中。 使用 ANTS Memory Profiler 的结果是我可以看到有泄漏,但我仍然不知道为什么。

此代码在 1 分钟(约 200k 次迭代)后导致 StackOverflowException 中的 StackOverflowException

An unhandled exception of type 'System.StackOverflowException' occurred in EntityFramework.dll

在非托管内存中,我可以看到大约 200k 个大小为 8192 字节的对象。

这不是不切实际的情况。真正阻止我的是真正的问题。

这是在我的真实软件中发生的事情的非常基本的模型。我首先需要解析数百万行不同格式的行,然后检查数据库中是否存在记录。

要重现您需要的问题: 主程序:

using System;
using System.Linq;

namespace ConsoleApplication31
{
    class Program
    {
        static void Main(string[] args)
        {
            var test = new TestClass();

            test.Test();
            Console.ReadLine();
        }
    }

    public class TestClass
    {
        public void Test()
        {
            try
            {
                for (int i = 0; i < 1000000; i++)
                {
                    using (var db = new TestEntity())
                    {
                        db.Configuration.AutoDetectChangesEnabled = false;
                        db.Configuration.ValidateOnSaveEnabled = false;
                        {
                            var any = (bool)db.Test.AsNoTracking().Any();
                        }
                    }
                    if (i % 1000 == 0)
                    {
                        Console.WriteLine(i.ToString());
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}

和数据库模型实体:

namespace ConsoleApplication31
{
    using System.Data.Entity;

    public partial class TestEntity : DbContext
    {
        public TestEntity()
                : base("name=TestEntityConnectionString")
        {
        }

        public virtual DbSet<Test> Test { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Database.SetInitializer<TestEntity>(null);
        }
    }
}

带测试台:

using System.ComponentModel.DataAnnotations;

namespace ConsoleApplication31
{
    public partial class Test
    {
            [Key]
            [Required]
            public long TestId { get; set; }

            [Required]
            public string TestName { get; set; }

            [Required]
            public string TestDescription { get; set; }
        }
}

我的配置:

a) 4C/8T 16GB RAM、Windows 10 14393 (RTM)、Visual Studio 2015、Sql Server 2016

b) 2C/4T 8GB RAM,完全相同的系统(相同的磁盘,连接到其他机器)

在 .NET Framework 版本上测试:4.5、4.5.2、4.6.1、4.6.2

在实体框架版本上测试:6.0.0、6.1.3

我将不胜感激。

当内存使用量约为 700MB 时调用堆栈: [托管到本地转换]

    System.Data.dll!SNINativeMethodWrapper.SNIReadSyncOverAsync(System.Runtime.InteropServices.SafeHandle pConn = {System.Data.SqlClient.SNIHandle}, ref System.IntPtr packet = {System.IntPtr}, int timeout)   Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()   Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()   Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()   Unknown
    System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryReadByte(out byte value = 0)  Unknown
    System.Data.dll!System.Data.SqlClient.TdsParser.TryRun(System.Data.SqlClient.RunBehavior runBehavior = ReturnImmediately, System.Data.SqlClient.SqlCommand cmdHandler = {System.Data.SqlClient.SqlCommand}, System.Data.SqlClient.SqlDataReader dataStream = {System.Data.SqlClient.SqlDataReader}, System.Data.SqlClient.BulkCopySimpleResultSet bulkCopyHandler = null, System.Data.SqlClient.TdsParserStateObject stateObj, out bool dataReady = false)  Unknown
    System.Data.dll!System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()    Unknown
    System.Data.dll!System.Data.SqlClient.SqlDataReader.MetaData.get()  Unknown
    System.Data.dll!System.Data.SqlClient.SqlCommand.FinishExecuteReader(System.Data.SqlClient.SqlDataReader ds = {System.Data.SqlClient.SqlDataReader}, System.Data.SqlClient.RunBehavior runBehavior, string resetOptionsString, bool isInternal, bool forDescribeParameterEncryption)    Unknown
    System.Data.dll!System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(System.Data.CommandBehavior cmdBehavior, System.Data.SqlClient.RunBehavior runBehavior, bool returnStream, bool async, int timeout, out System.Threading.Tasks.Task task, bool asyncWrite, bool inRetry, System.Data.SqlClient.SqlDataReader ds, bool describeParameterEncryptionRequest)  Unknown
    System.Data.dll!System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior cmdBehavior, System.Data.SqlClient.RunBehavior runBehavior, bool returnStream, string method, System.Threading.Tasks.TaskCompletionSource<object> completion, int timeout, out System.Threading.Tasks.Task task, out bool usedCache, bool asyncWrite, bool inRetry)   Unknown
    System.Data.dll!System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior cmdBehavior, System.Data.SqlClient.RunBehavior runBehavior, bool returnStream, string method) Unknown
    System.Data.dll!System.Data.SqlClient.SqlCommand.ExecuteReader(System.Data.CommandBehavior behavior, string method) Unknown
    System.Data.dll!System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(System.Data.CommandBehavior behavior)  Unknown
    System.Data.dll!System.Data.Common.DbCommand.ExecuteReader(System.Data.CommandBehavior behavior)    Unknown
    EntityFramework.dll!System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader.AnonymousMethod__c(System.Data.Common.DbCommand t, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader> c)    Unknown
    EntityFramework.dll!System.Data.Entity.Infrastructure.Interception.InternalDispatcher<System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor>.Dispatch<System.Data.Common.DbCommand, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader>, System.Data.Common.DbDataReader>(System.Data.Common.DbCommand target, System.Func<System.Data.Common.DbCommand, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader>, System.Data.Common.DbDataReader> operation, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext, System.Action<System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor, System.Data.Common.DbCommand, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader>> executing, System.Action<System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor, System.Data.Common.DbCommand, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader>> executed)   Unknown
    EntityFramework.dll!System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext)    Unknown
    EntityFramework.dll!System.Data.Entity.Internal.InterceptableDbCommand.ExecuteDbDataReader(System.Data.CommandBehavior behavior)    Unknown
    System.Data.dll!System.Data.Common.DbCommand.ExecuteReader(System.Data.CommandBehavior behavior)    Unknown
    EntityFramework.dll!System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(System.Data.Entity.Core.EntityClient.EntityCommand entityCommand, System.Data.CommandBehavior behavior)  Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlan.Execute<bool>(System.Data.Entity.Core.Objects.ObjectContext context = {System.Data.Entity.Core.Objects.ObjectContext}, System.Data.Entity.Core.Objects.ObjectParameterCollection parameterValues) Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.ObjectQuery<bool>.GetResults.AnonymousMethod__6()   Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction<System.__Canon>(System.Func<System.__Canon> func, System.Data.Entity.Infrastructure.IDbExecutionStrategy executionStrategy, bool startLocalTransaction, bool releaseConnectionOnSuccess = false) Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.ObjectQuery<bool>.GetResults.AnonymousMethod__5()   Unknown
    EntityFramework.SqlServer.dll!System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute<System.Data.Entity.Core.Objects.ObjectResult<bool>>(System.Func<System.Data.Entity.Core.Objects.ObjectResult<bool>> operation)   Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.ObjectQuery<bool>.GetResults(System.Data.Entity.Core.Objects.MergeOption? forMergeOption)   Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.ObjectQuery<bool>.System.Collections.Generic.IEnumerable<T>.GetEnumerator.AnonymousMethod__0()  Unknown
    EntityFramework.dll!System.Data.Entity.Internal.LazyEnumerator<bool>.MoveNext() Unknown
    System.Core.dll!System.Linq.Enumerable.Single<bool>(System.Collections.Generic.IEnumerable<bool> source)    Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.GetElementFunction.AnonymousMethod__3<bool>(System.Collections.Generic.IEnumerable<bool> sequence)    Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.ExecuteSingle<bool>(System.Collections.Generic.IEnumerable<bool> query, System.Linq.Expressions.Expression queryRoot) Unknown
    EntityFramework.dll!System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.Execute<bool>(System.Linq.Expressions.Expression expression)  Unknown
    EntityFramework.dll!System.Data.Entity.Internal.Linq.DbQueryProvider.Execute<bool>(System.Linq.Expressions.Expression expression)   Unknown
    System.Core.dll!System.Linq.Queryable.Any<ConsoleApplication31.Test>(System.Linq.IQueryable<ConsoleApplication31.Test> source)  Unknown
>   ConsoleApplication31.exe!ConsoleApplication31.TestClass.Test() Line 31  C#
    ConsoleApplication31.exe!ConsoleApplication31.Program.Main(string[] args = {string[0]}) Line 12 C#
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) Unknown
    Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()   Unknown
    mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state)    Unknown
    mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
    mscorlib.dll!System.Threading.ThreadHelper.ThreadStart()    Unknown

【问题讨论】:

  • 尝试在循环之外实例化您的 TestEntity 实例。您正在非常快速地创建大量实例。
  • @Dispersia:我确实尝试过,但没有帮助。
  • 这真的没有多大意义。 StackOverflowException 通常只在有too many nested method calls 时出现。你能在中途中断应用程序并检查调用堆栈吗?
  • 请问错误的调用堆栈是什么?
  • @ErikEJ 您无法获取 SO 异常的调用堆栈。

标签: c# sql-server entity-framework memory-leaks ants


【解决方案1】:

我仍然不知道为什么会出现这个问题,但现在我知道它与我的 Sql Server 安装有关。

当我使用安装在 Hyper-V 中的 Sql Server 时,一切似乎都很好。

只要我将连接字符串更改为指向我的本地 Sql 服务器实例,我就会遇到这个奇怪的泄漏。

现在这对我来说是某种解决方案,至少我可以继续前进。

【讨论】:

  • 会不会和你的客户端库如何连接SQL服务器有关?本地 SQL 服务器有比远程 SQL 服务器更多的选项,例如命名管道。您说泄漏发生在非托管内存中,所以我认为这可能是由与连接相关的错误引起的。尝试修改 ConnectionString 以关闭或修改连接池,看看是否有任何改变。尝试指定应该使用 tcp 进行连接。 msdn doc
猜你喜欢
  • 2013-10-21
  • 2012-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-09
  • 2020-09-22
  • 2018-06-04
  • 2011-03-25
相关资源
最近更新 更多