C#System.Buffer.BlockCopy内存问题?

C#System.Buffer.BlockCopy内存问题?,c#,memory-leaks,garbage-collection,buffer,C#,Memory Leaks,Garbage Collection,Buffer,我有一个困扰我几天的问题。我曾尝试用谷歌搜索这个问题,但迄今为止还没有找到任何解决方案,甚至没有一个人有同样的问题 似乎C#method System.Buffer.BlockCopy给您留下了某种内存重影。例如,我有以下方法: private float[,] readFloatArray2 (byte[] b) { int floatSize = sizeof(float); float[,] v = new float[2, (b.Length / 2) / floatSize

我有一个困扰我几天的问题。我曾尝试用谷歌搜索这个问题,但迄今为止还没有找到任何解决方案,甚至没有一个人有同样的问题

似乎C#method System.Buffer.BlockCopy给您留下了某种内存重影。例如,我有以下方法:

private float[,] readFloatArray2 (byte[] b) {
   int floatSize = sizeof(float);
   float[,] v = new float[2, (b.Length / 2) / floatSize];
   System.Buffer.BlockCopy(b, 0, v, 0, b.Length);

   return v;
}
将字节数组转换为二维浮点数组。数据先前是从流中读取的。 我已确定问题出在System.Buffer.BlockCopy方法上


如果我删除BlockCopy命令,应用程序使用的内存将是大小的一半。这意味着字节数组仍然存在不是我的错。因为没有BlockCopy命令,字节数组将正常消亡。无论如何都会创建浮点数组(有或没有复制的信息)

我不确定这是BlockCopy命令还是GC的问题,因为我也尝试调用System.GC.Collect();在区块复制之后,它也可以完美地工作(我知道你不应该这样做……这就是我在这里寻求建议的原因)

我也不想费心问,但这个问题涉及几百兆

除了内存问题外,该方法工作得非常好。有人知道是什么导致了记忆问题吗

提前问候和感谢 奥利


ps:我正在使用.NET4.0与Visual Studio 2010 PRO和WIN7。。。不知道这是否相关。

BlockCopy没有托管的.NET实现。在内部,它调用外部WinAPI

[SecuritySafeCritical]
public static extern void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count);

Buffer.BlockCopy
基于字节而不是基于索引。我建议您使用Array.Copy,它的作用基本相同。BlockCopy只是稍微快一点

首先需要将字节[]转换为浮点[]。请查看下面的说明

static float[] ConvertByteArrayToFloat(byte[] bytes)
{
    if(bytes == null)
        throw new ArgumentNullException("bytes");

   if(bytes.Length % 4 != 0)
        throw new ArgumentException
              ("bytes does not represent a sequence of floats");

    return Enumerable.Range(0, bytes.Length / 4)
                     .Select(i => BitConverter.ToSingle(bytes, i * 4))
                     .ToArray();
}
我发现问题出在
System.Buffer.BlockCopy
方法上。如果我删除
BlockCopy
命令,应用程序使用的内存将是大小的一半。这意味着字节数组仍然存在不是我的错。因为没有
BlockCopy
命令,字节数组将正常消亡

我不同意这个结论。我看到几个阶段:

  • 字节数组已存在并已填充数据
  • 您分配了浮点数组,但它还没有填充数据
  • 用数据填充浮点数组
  • 字节数组不再被引用,但尚未被收集
  • 已收集字节数组
  • 字节数组的活动不受
    BlockCopy
    的影响

    步骤2保留并提交虚拟内存。因此,在这一步中,提交大小会增加。但由于数组的内容从未写入,并且完全由
    00
    字节组成,因此windows内存管理器不会为其分配任何物理内存。它只是注意到这些页面完全由
    00
    s组成

    浮点数组的物理内存仅在步骤3中分配。如果添加一个初始化数组中每个字段的循环,则会得到相同的效果


    除了实际问题外,我还有一些设计建议:

  • 重用缓冲区。GC适用于小型短期对象,但对大型短期对象非常不利。这意味着您不应该使用分配和返回大型数组的函数。取而代之的是将它们作为参数,这样就可以重用现有的数组
  • 如果您使用的是音频数据(似乎有可能),我不会使用实心2D阵列。相反,我会使用数组的数组。其中,内部数组表示单个缓冲区中的样本,外部数组表示缓冲区。这有两个好处:

    • 您可以轻松编写只在单个通道上运行的代码
    • 实体2D阵列索引速度较慢,因此通常也更快
  • 是否确实要一次读取所有数据?我读了几千字节的数据
  • 我不确定这是BlockCopy命令还是GC的问题,因为我也尝试调用System.GC.Collect();在区块复制之后,它也可以完美地工作(我知道你不应该这样做……这就是我在这里寻求建议的原因)。我也不会费心去问,我们谈论的不是几百兆字节

    当特定生成需要更多内存时,或从LOH运行垃圾收集。一般来说,垃圾收集不会仅仅因为有垃圾要收集而运行,而且通常这是一件好事(只要GC在我们确实需要的时候能够得到它,我们就不会花费任何费用来正式“使用”我们不使用的千兆字节内存)

    有时调用
    GC.Collect()
    在实际程序中是有意义的,这很可能是其中之一,因此如果这样做“效果很好”,那么我就不会太担心99.9%的代码会违反最佳实践。这是“最佳实践”而不是硬性规定的原因是,有时我们处于0.1%的情况下,通常的最佳实践不再是最佳实践


    此外,如果您可以提前预测数组的最大大小(或者如果不能预测,仅源字节数组),那么CodeInChaos的第一种方法可能会奏效。使用10000000字节来处理32实际上并没有什么坏处,只要在某个时候您确实会使用10000000字节。重新使用10000000可以在流程的整个生命周期内实现真正的节约。

    ok。。。如何处理这个问题?你有什么建议?没什么。当它需要额外的内存时,GC最终会处理它。如果你说内存是由
    GC.Collect
    正确收集的,那么一切都很好
    b
    最终将由普通GC在适当的时候收集。如果您正在处理音频数据,我将使用
    floa形式的锯齿数组