繁体   English   中英

数组 c# 的内存泄漏

[英]Memory leak with an array c#

用户指定文件名和块大小。 原始文件以用户块大小拆分成块(最后一个块除外)。 对于每个块计算哈希函数SHA256并写入控制台。

这是一个有 2 个线程的程序:第一个线程读取原始文件并放入块的队列字节数组中; 第二个线程从队列中删除块的字节数组并计算哈希。

在第一次迭代之后,直到程序完成,内存才会处理。
在下一次迭代中,内存正常分配和处置。
因此,在下一次读取部分数组时,我得到OutOfMemoryException

如何正确管理内存以避免内存泄漏?

class Encryption
{
    static FileInfo originalFile;
    static long partSize = 0; 
    static long lastPartSize = 0; 
    static long numParts = 0; 
    static int lastPartNumber = 0; 
    static string[] hash; 
    static Queue<byte[]> partQueue = new Queue<byte[]>();

    public Encryption(string _filename, long _partSize)
    {
        try
        {
            originalFile = new FileInfo(@_filename);
            partSize = _partSize;

            numParts = originalFile.Length / partSize; 
            lastPartSize = originalFile.Length % partSize; 

            if (lastPartSize != 0)
            {
                numParts++;
            }
            else if (lastPartSize == 0)
            {
                lastPartSize = partSize;
            }

            lastPartNumber = (int)numParts - 1;

            hash = new string[numParts];
        }
        catch (FileNotFoundException fe)
        {
            Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace);
            return;
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace);
        }
    }

    private void readFromFile()
    {
        try
        {
            using (FileStream fs = new FileStream(originalFile.FullName, FileMode.Open, FileAccess.Read))
            {
                for (int i = 0; i < numParts; i++)
                {                        
                    long len = 0;

                    if (i == lastPartNumber)
                    {
                        len = lastPartSize;
                    }
                    else
                    {
                        len = partSize;
                    }                            

                    byte[] part = new byte[len];                

                    fs.Read(part, 0, (int)len);

                    partQueue.Enqueue(part);

                    part = null;
                }
            }
        }   
        catch(Exception e)
        {
            Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace);
        }    
    }

    private static void hashToArray()
    {
        try
        {
            SHA256Managed sha256HashString = new SHA256Managed(); 
            int numPart = 0;

            while (numPart < numParts)
            {
                long len = 0;
                if (numPart == lastPartNumber)
                {
                    len = lastPartSize;
                }
                else
                {
                    len = partSize;
                }

                hash[numPart] = sha256HashString.ComputeHash(partQueue.Dequeue()).ToString();

                numPart++;
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace);
        }
    }

    private void hashWrite()
    {
        try
        {
            Console.WriteLine("\nResult:\n");                
            for (int i = 0; i < numParts; i++)
            {
                Console.WriteLine("{0} : {1}", i, hash[i]);
            }
        }
        catch(Exception e)
        {
            Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace);
        }
    }

    public void threadsControl()
    {
        try
        {
            Thread readingThread = new Thread(readFromFile);
            Thread calculateThread = new Thread(hashToArray);

            readingThread.Start();
            calculateThread.Start();

            readingThread.Join();
            calculateThread.Join();

            hashWrite();
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: {0}\nStackTrace: {1}", fe.Message, fe.StackTrace);
        }
    }
}

在编写此类代码之前,您应该阅读一些有关 .NET 内部结构的书籍。 您对 .NET 内存模型的理解完全错误,这就是您收到此类错误的原因。 如果您关心您的资源,尤其是在处理数组时, OutOfMemoryException很少发生。

您应该知道,在 .NET 运行时中,引用对象有两种堆,基本的一种和大对象堆,它们之间最重要的区别是即使在垃圾收集之后 LOH 也不会被压缩

你应该知道所有的数组,即使是小的数组,都会进入 LOH,内存消耗非常快。 你也应该知道这一行:

part = null;

不会立即处理内存 更糟糕的是,这一行根本没有做任何事情,因为您仍然可以引用您在队列中读取的文件部分。 这就是你的记忆消失的原因。 您可以尝试通过在每次哈希计算后调用GC来解决此问题,但这是强烈不推荐的解决方案

您应该重写您的算法(这是Producer/Consumer模式的非常简单的情况),而不要同时将整个文件内容存储在内存中。 这很容易 - 只需将您的part变量移出静态字段,然后将下一个文件部分读入其中。 在您的代码而不是队列中引入EventWaitHandle (或其子类之一),并在您阅读文件的下一部分后立即计算下一个哈希。

我建议您通过阅读Joe Albahari精彩系列,从 C# 线程的基础知识开始,然后才尝试实现此类解决方案。 祝你的项目好运。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM