2

我现在正在编写一个 Windows Store App Reader,用于使用 C# 查看 PDF 文件。

目前,我正在尝试解决可能导致内存泄漏的问题。在应用程序中,有一种加密和解密PDF文件的方法,加密/解密方法是自定义方法,PDF文件大小在1MB到120MB之间。

在加解密过程中,必须将文件以字节数组的形式读入内存,并对字节进行加解密处理。从方法返回后,字节数组超出范围。但似乎因为它是一个大字节数组(> 85k),我认为它是在大对象堆(LOH)中声明的,并且在收集垃圾之后,看起来 LOH 变得碎片化。

并且在应用打开、查看和切换更多 PDF 文件后,内存使用率越来越高,并且在某些时候会出现内存不足的异常。

有没有办法解决大字节数组问题?或者有人遇到过这样的问题吗?

我找到了 Memory Mapped Files 类,但它不能在 Windows Store App 中使用。

我也尝试过使用字节数组块(<85k)并逐部分处理文件,但在查看 PDF 之前加密和解密需要很长时间。

下面是代码片段:
public static async Task CryptoFile(StorageFolder Folder, string FileName, bool IsReaderFile)
{
...
...

byte[] fileContent = null;
buffer = await FileIO.ReadBufferAsync(file);
using (DataReader dataReader = DataReader.FromBuffer(buffer))
{
        fileContent = new byte[dataReader.UnconsumedBufferLength];
    dataReader.ReadBytes(fileContent);
}

if (fileContent == null)
        CryptResult = false;
else if (fileContent.Length == 0)
        CryptResult = false;

if (CryptResult)
{
        //Encrypt/decrypt file
    fileContent = await RecodeFile(fileContent, CryptKey, IsReaderFile);

    //Delete the original file
    file = await Folder.GetFileAsync(FileName);
    await file.DeleteAsync(StorageDeleteOption.PermanentDelete);

    //Recreate file
    file = await Folder.CreateFileAsync(FileName);
    using (IRandomAccessStream fs = await file.OpenAsync(FileAccessMode.ReadWrite))
    {
            using (IOutputStream outStream = fs.GetOutputStreamAt(0))
        {
                using (DataWriter dataWriter = new DataWriter(outStream))
            {
                    dataWriter.WriteBytes(fileContent);
                await dataWriter.StoreAsync();
                dataWriter.DetachStream();
            }

            await outStream.FlushAsync();
        }
    }

    ....
    }

}

fileContent 是用于读取整个文件内容的字节数组,从该方法返回时它将超出范围,我认为 fileContent 会导致内存碎片,因为它可能在 LOH 中声明它的大小。

4

1 回答 1

0

假设您的问题与 LOH 有关,您是否考虑过将较小的阵列包装成 LOH 友好的阵列替代品。如果您愿意将块大小限制为 2 的幂,则可以进一步优化示例。然后 div mod 变成了一个移位和一个位掩码。显然,如果需要,您可以实现所有 ICollection 的东西。

我以前用过这种方法,性能还算合理。

public sealed class ArrayRope<T>
{
    private readonly T[][] map;

    private readonly int blockSize;
    private readonly int noOfBlocks;

    public ArrayRope(int blockSize, int noOfBlocks)
    {
        ...
    }

    public T this[int index]
    {
        get
        {
            int ropeIndex = index / blockSize;
            int offset = index % blockSize;

            return map[ropeIndex][offset];
        }
        set
        {
            int ropeIndex = index / blockSize;
            int offset = index % blockSize;

            map[ropeIndex][offset] = value;
        }
    }

    ...
}
于 2013-06-27T07:12:42.823 回答