C#内存开销从何而来
我这里有点资源问题。似乎.NET正在产生大量的内存开销,并且/或者没有释放出它本不需要的内存。但问题在于: 我有一个对象,它读取以下类的STL文件:C#内存开销从何而来,c#,memory,C#,Memory,我这里有点资源问题。似乎.NET正在产生大量的内存开销,并且/或者没有释放出它本不需要的内存。但问题在于: 我有一个对象,它读取以下类的STL文件: public class cSTLBinaryDataModel { public byte[] header { get; private set; } public UInt32 triangleCount { get { return Convert.ToUInt32(triangleList.Count); } }
public class cSTLBinaryDataModel
{
public byte[] header { get; private set; }
public UInt32 triangleCount { get { return Convert.ToUInt32(triangleList.Count); } }
public List<cSTLTriangle> triangleList { get; private set; }
public cSTLBinaryDataModel()
{
header = new byte[80];
triangleList = new List<cSTLTriangle>();
}
public void ReadFromFile(string in_filePath)
{
byte[] stlBytes;
//Memory logpoint 1
stlBytes = File.ReadAllBytes(in_filePath);
//Memory logpoint 2
ReadHeader(stlBytes.SubArray(0, cConstants.BYTES_IN_HEADER));
ReadTriangles(stlBytes.SubArray(cConstants.BYTES_IN_HEADER, stlBytes.Length - cConstants.BYTES_IN_HEADER));
//Evaluate memory logpoints here
}
private void ReadHeader(byte[] in_header)
{
header = in_header;
}
private void ReadTriangles(byte[] in_triangles)
{
UInt32 numberOfTriangles = BitConverter.ToUInt32(cHelpers.HandleLSBFirst(in_triangles.SubArray(0, 4)), 0);
//Memory logpoint 3
for (UInt32 i = 0; i < numberOfTriangles; i++)
{
triangleList.Add(new cSTLTriangle(in_triangles.SubArray(Convert.ToInt32(i * cConstants.BYTES_PER_TRIANGLE + 4), Convert.ToInt32(cConstants.BYTES_PER_TRIANGLE))));
}
//Memory logpoint 4
}
}
类cVector
为:(为这么多代码感到抱歉)
如果我计算我的类中使用的类型的大小,那么对于cSTLTriangle
的一个实例,它总共是51个字节。我知道必须有一个开销来容纳功能等。但是,如果我将这个大小乘以三角形的数量,结果是512,3MB,这与实际的文件大小非常一致。我可以想象三角形列表
占用的内存量大致相同(同样考虑到轻微的开销,它是一个列表
无),但不是!(使用GC.GetTotalMemory(false)计算内存)
从Logpoint 1到Logpoint 2,增加了526660800字节,这相当准确地表示加载到字节数组中的STL文件的大小。
Logpoint 3和Logpoint 2之间的增量大致相同,这是可以理解的,因为我将子数组传递给ReadTriangles
方法。子数组
是我在这里找到的代码,所以(这可能是伪装中的魔鬼吗?)
公共静态T[]子数组(此T[]数据,int索引,int长度)
{
T[]结果=新的T[长度];
复制(数据、索引、结果、0、长度);
返回结果;
}
事情在下一个关键时刻变得荒谬可笑。在Logpoint 4和Logpoint 3之间,内存使用量大约增加了原始STL文件大小的4.73倍(如您所见,在解析每个三角形时,我大量使用.SubArray
)
当我让程序继续运行时,内存使用没有显著增加:好,但也没有减少:坏。我希望保存文件的字节[]
会释放内存,因为它超出了范围,我传递给ReadTriangles(字节[]…)
的子数组也超出了范围,但不知怎的,它们没有。我最终得到的“开销”是原始STL数据大小的5.7倍
这是正常的行为吗?NET运行时是否会像Photoshop一样,在获得一些jucy RAM后保持内存分配(即使已扩展到磁盘)?如何减少此类组合的内存占用?
编辑:
- 问题发生在Win7 Enterprise x64上。NET 4.5,为x64编译(也为AnyCPU编译)
- 在对象创建完成后(在对象本身之外),我调用了
,但什么也没有发生。只有在将对象引用设置为GC.Collect()
之后,我才恢复了内存null
byte[] header
byte[] triangles
在分割完原始字节数组后,将其设置为null,然后可以使用System.GC.Collect()
强制垃圾收集器运行。这将为您节省一点内存
public void ReadFromFile(string in_filePath)
{
byte[] stlBytes;
//Memory logpoint 1
stlBytes = File.ReadAllBytes(in_filePath);
//Memory logpoint 2
byte[] header = stlBytes.SubArray(0, cConstants.BYTES_IN_HEADER);
byte[] triangles = stlBytes.SubArray(cConstants.BYTES_IN_HEADER, stlBytes.Length - cConstants.BYTES_IN_HEADER);
ReadHeader(header);
ReadTriangles(triangles);
stlBytes = null;
System.GC.Collect();
//Evaluate memory logpoints here
}
有几件事可以尝试减少内存使用 首先,如果可能的话,您应该重写文件加载代码,以便它只加载所需的数据,而不是一次加载整个文件 例如,可以将标题作为单个块读取,然后将每个三角形的数据作为单个块(在循环中)读取 第二,您的大对象堆可能正在经历碎片化,垃圾收集器不会移动大对象,因此无法对其进行碎片化。(如果.Net 4.51修复了此问题,则必须显式启用大型对象堆碎片整理,并显式启动它。) 您可以通过预先调整
三角形列表的大小来缓解此问题
此时,您依次将每个三角形添加到三角形列表
,从一个容量为零的列表开始。这意味着每隔一段时间列表的容量就会被超过,导致列表被扩展
当列表处于满负荷状态时,通过向列表中添加项目来展开列表时,它将:
- 创建两倍于当前缓冲区大小的新内部缓冲区
- 将旧缓冲区复制到新缓冲区
- 删除旧的缓冲区
- 将新项复制到新缓冲区
问题有两个方面:
大量冗余复制正在进行
如果内部缓冲区超过将对象放入大型对象堆的阈值,则可能会得到堆碎片
由于您事先知道三角形列表的最大大小,因此可以通过在添加项目之前设置列表的容量来解决此问题:
triangleList.Capacity = numberOfTriangles;
内存开销
您的cVector
类增加了大量内存开销。在32位系统上,如果我没有记错的话,任何引用对象都有12个字节的内存开销(尽管其中4个字节可以由字段自由使用)。让我们使用8字节的开销。因此,对于10000000个三角形,每个三角形包含4个向量,这将upp添加到:
10000000*4*8=305 MB的开销
如果您在64位系统上运行,则该值是以下值的两倍:
10000000*4*16=610 MB的开销
除此之外,您还有四个参考的开销,每个cSTLTriangle
将有四个参考向量,为您提供:
10000000*4*4=152 MB(32位)
10000000*4*8=305 MB(64位)
正如您所看到的,所有这些都会增加相当大的开销
因此,在本例中,我建议您将cVector
astruct
。如注释中所述,结构可以实现接口(以及属性和方法)。请注意@Jcl提到的那个一点
您的cSTLTriangle
类也有同样的问题(对于32-b,大约有76/152 MB的开销)
byte[] header
byte[] triangles
public void ReadFromFile(string in_filePath)
{
byte[] stlBytes;
//Memory logpoint 1
stlBytes = File.ReadAllBytes(in_filePath);
//Memory logpoint 2
byte[] header = stlBytes.SubArray(0, cConstants.BYTES_IN_HEADER);
byte[] triangles = stlBytes.SubArray(cConstants.BYTES_IN_HEADER, stlBytes.Length - cConstants.BYTES_IN_HEADER);
ReadHeader(header);
ReadTriangles(triangles);
stlBytes = null;
System.GC.Collect();
//Evaluate memory logpoints here
}
triangleList.Capacity = numberOfTriangles;