使用非常大的字符串时,C#中的内存泄漏

时间:2018-03-15 10:46:14

标签: c# string memory-leaks

检查我的某个应用程序的内存泄漏,我发现下一个代码“表现得很奇怪”。

public String DoTest()
        {
            String fileContent = "";
            String fileName = "";
            String[] filesNames = System.IO.Directory.GetFiles(logDir);
            List<String> contents = new List<string>();

            for (int i = 0; i < filesNames.Length; i++)
            {
                fileName = filesNames[i];
                if (fileName.ToLower().Contains("aud"))
                {
                    contents.Add(System.IO.File.ReadAllText(fileName));
                }
            }
            fileContent = String.Join("", contents);
            return fileContent;
        }

在运行这段代码之前,对象使用的内存大约为1.4 Mb。一旦调用此方法,它就使用了70MB。等待几分钟,没有任何改变(原始物体很久以前就被释放了) 打电话给

GC.Collect();
GC.WaitForFullGCComplete();

将内存减少到21MB(然而,远远超过开始时的1.4MB)。

使用控制台应用程序(无限循环)和winform应用程序进行测试。甚至在直接调用时也会发生(不需要创建更多对象)。

编辑:完整代码(控制台应用)以显示问题

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace memory_tester
{

    /// <summary>
    /// Class to show loosing of memory
    /// </summary>
    class memory_leacker
    {
        // path to folder with 250 text files, total of 80MB of text
        const String logDir = @"d:\http_server_test\http_server_test\bin\Debug\logs\";

        /// <summary>
        /// Collecting all text from files in folder logDir and returns it.
        /// </summary>
        /// <returns></returns>
        public String DoTest()
        {
            String fileContent = "";
            String fileName = "";
            String[] filesNames = System.IO.Directory.GetFiles(logDir);
            List<String> contents = new List<string>();

            for (int i = 0; i < filesNames.Length; i++)
            {
                fileName = filesNames[i];
                if (fileName.ToLower().Contains("aud"))
                {
                    //using string builder directly into fileContent shows same results.
                    contents.Add(System.IO.File.ReadAllText(fileName));

                }
            }
            fileContent = String.Join("", contents);
            return fileContent;
        }

        /// <summary>
        /// demo call to see that no memory leaks here
        /// </summary>
        /// <returns></returns>
        public String DoTestDemo()
        {
            return "";
        }

    }

    class Program
    {
        /// <summary>
        /// Get current proc's private memory
        /// </summary>
        /// <returns></returns>
        public static long GetUsedMemory()
        {
            String procName = System.AppDomain.CurrentDomain.FriendlyName;
            long mem = Process.GetCurrentProcess().PrivateMemorySize64 ;
            return mem;
        }

        static void Main(string[] args)
        {
            const long waitTime = 10;  //was 240
            memory_leacker mleaker = new memory_leacker();

            for (int i=0; i< waitTime; i++)
            {
                Console.Write($"Memory before {GetUsedMemory()} Please wait {i}\r");
                Thread.Sleep(1000);
            }
            Console.Write("\r\n");

            mleaker.DoTestDemo();
            for (int i = 0; i < waitTime; i++)
            {
                Console.Write($"Memory after demo call {GetUsedMemory()} Please wait {i}\r");
                Thread.Sleep(1000);
            }
            Console.Write("\r\n");

            mleaker.DoTest();
            for (int i = 0; i < waitTime; i++)
            {
                Console.Write($"Memory after real call {GetUsedMemory()} Please wait {i}\r");
                Thread.Sleep(1000);
            }
            Console.Write("\r\n");

            mleaker = null;
            for (int i = 0; i < waitTime; i++)
            {
                Console.Write($"Memory after release objectg {GetUsedMemory()} Please wait {i}\r");
                Thread.Sleep(1000);
            }
            Console.Write("\r\n");

            GC.Collect();
            GC.WaitForFullGCComplete();
            for (int i = 0; i < waitTime; i++)
            {
                Console.Write($"Memory after GC {GetUsedMemory()} Please wait {i}\r");
                Thread.Sleep(1000);
            }
            Console.Write("\r\n...pause...");

            Console.ReadKey();
        }
    }
}

2 个答案:

答案 0 :(得分:2)

我相信如果你在fileContent上使用stringbuilder而不是字符串,你可以提高你的性能和内存使用。

public String DoTest()
        {
            var fileContent = new StringBuilder();
            String fileName = "";
            String[] filesNames = System.IO.Directory.GetFiles(logDir);
            for (int i = 0; i < filesNames.Length; i++)
            {
                fileName = filesNames[i];
                if (fileName.ToLower().Contains("aud"))
                {
                    fileContent.Append(System.IO.File.ReadAllText(fileName));
                }
            }

            return fileContent;
        }

答案 1 :(得分:-2)

我在下面重构了你的代码版本,在这里我已经删除了原始问题中名为“contents”的字符串列表的需要。

    public String DoTest()
    {
        string fileContent = "";

        IEnumerable<string> filesNames = System.IO.Directory.GetFiles(logDir).Where(x => x.ToLower().Contains("aud"));

        foreach (var fileName in filesNames)
        {
            fileContent = string.Join("", System.IO.File.ReadAllText(fileName));
        }

        return fileContent;
    }