2012-01-03 166 views
6

我有一个问题,现在一直困扰着我几天。我试图谷歌这个问题,但到目前为止还没有找到任何解决方案,甚至没有一个人有同样的问题。C#System.Buffer.BlockCopy内存问题?

看来,C#方法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; 
} 

将字节数组转换为2D浮点数组。数据先前从流中读取。 我找到了问题是System.Buffer.BlockCopy方法。

如果我删除了BlockCopy命令,则应用程序使用的内存将减半。这意味着它不是我的错,即字节数组仍然存在。因为没有BlockCopy命令,字节数组就会正常死亡。浮动数组无论如何被创建(有或没有复制的信息)。

我不太确定这是BlockCopy命令还是GC的问题,因为我也尝试调用System.GC.Collect(); BlockCopy之后,然后它也可以完美运行(我知道你不应该这样做...这就是为什么我要求在这里建议)。

我也不会打扰问,但问题涉及几百兆。

除了内存问题,该方法工作得很好。有谁知道是什么导致了内存问题?

的问候和感谢事先 OLI

PS:我使用的.NET4.0与Visual Studio 2010 PRO和WIN7 ...不知道这是否是相关或不。

+2

如果你说这个内存被一个'GC.Collect'正确收集,那么一切都很好。当时间到时,'b' *将最终由普通的GC收集。 – 2012-01-03 10:11:09

+0

如果您正在处理音频数据,我会使用形式为float [len] [channelCount]'的锯齿状数组。这样你可以分开处理通道,这有时候很有用。 – CodesInChaos 2012-01-03 11:07:22

+1

“浮点数组无论如何被创建”只有一半是真的。它不一定需要物理内存。内存页面全部为0,并且从未被写入,它们都由Windows内存管理器优化。 – CodesInChaos 2012-01-03 11:09:53

回答

1

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

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

好的...以及如何处理这个问题?你有什么建议? – 2012-01-03 10:07:47

+0

什么都没有。当GC需要额外的内存时,GC最终会照顾它。 – 2012-01-03 10:13:56

1

Buffer.BlockCopy是基于字节而不是基于索引的。我建议你使用Array.Copy这基本上是做同样的事情。 BlockCopy稍微快一点。

需要字节[]在下面转换为浮动[] first.Have看看该

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(); 
} 
+0

但Array.Copy不适用于不同的数据类型。我不能用它从byte []复制到float [,]。 – 2012-01-03 10:56:46

+0

我编辑了我的答案 – 2012-01-03 11:03:39

+0

是啊...好吧...这很酷,但我有一个2D浮点数组(浮点数[1]字节数组)。但你的建议很酷。我不知道这一点。 (现在用C#编码4个月) – 2012-01-03 11:06:29

2

我已经找到了问题是System.Buffer.BlockCopy方法。如果我删除了BlockCopy命令,则应用程序使用的内存将减半。这意味着它不是我的错,即字节数组仍然存在。因为没有BlockCopy命令,字节数组正常死亡。

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

  1. 的字节数组存在,并且充满了数据
  2. 你分配的float数组,但它不填满数据呢。
  3. 您的数据填充
  4. 的字节数组不再被引用的int数组,但一直没有收集尚未
  5. 的字节数组已收集。

字节数组的实况不受BlockCopy的影响。

步骤2保留并提交虚拟内存。所以提交大小在这一步增长。但由于数组的内容从来没有被写入,并且完全由00字节组成,所以windows内存管理器不会为其分配任何物理内存。它只是指出这些页面完全由00组成。

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


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

  1. 重用缓冲区。气相色谱仪适用于小型短寿命物体,但对大型短寿物体非常不利。这意味着你不应该使用分配和返回大数组的函数。而应将它们作为参数,以便现有数组可以重用。
  2. 如果您正在使用音频数据(看起来很可能),我不会使用纯二维数组。我会使用一组数组。内部数组代表单个缓冲区中的样本,外部数组代表缓冲区。这有两个优点:

    • 您可以轻松编写只能在单个通道上运行的代码。
    • 实体二维数组索引较慢,因此速度通常也较快。
  3. 您是否真的想一次读取所有数据?我读了几千字节的大块。
+0

我是一个生物信息学所以,不,我不用音频文件。更像MS谱。我需要全局数据进行处理,因此设计建议1.和2.是没有选择的(步骤1是没有选择的,因为加速字节的长度差别很大(从32字节到> 10.000.000))。 设计建议3也不是一种选择,因为我必须不止一次处理数据的不同部分以突出不同的方面。因此我不需要重新装载所有东西,因为我需要整套信息直到过程结束。 但无论如何:) :) – 2012-01-03 13:11:19

+0

哦...并建议3,我不一次读取所有。我读频谱明智的。一个光谱大小可以从32B到> 10MB不等。我有大约80,000个光谱要读取。这些处理中的每一个都是必要的。 – 2012-01-03 13:27:24

1

我不能肯定这是否是BlockCopy命令的问题或GC,因为我自己也尝试拨打System.GC.Collect(); BlockCopy之后,然后它也可以完美运行(我知道你不应该这样做...这就是为什么我要求在这里建议)。我也不会问,如果它不是我们正在谈论的几个hundret MB。

当需要更多内存用于特定代或LOH时,垃圾回收会运行。通常垃圾收集不会仅仅因为垃圾收集而运行,并且通常这是一件好事(如果我们没有正式使用正式使用的千兆字节内存,我们并没有花费任何代价只要GC在需要时可以得到它)。

有些时候,在实际的程序中调用GC.Collect()确实有意义,这可能就是其中之一,所以如果这样做“完美地工作”,那么我不会过多担心它是对付最佳解决方案,练习99.9%的代码。它是“最佳实践”而不是一条硬性规定的原因是,有时我们处于0.1%的情况,通常最佳实践不再是最佳实践。另外,如果您可以提前预测数组的最大大小(或者仅仅是源字节数组的失败次数),那么CodeInChaos的第一种方法可能会起作用。使用10,000,000个字节来处理32,实际上并没有什么坏处,只要在某个时候你确实会使用那个10,000,000。重复使用10,000,000次可以在整个生命周期中实现非常实际的节约。