我在一些遗留代码中遇到内存问题 代码使用巨大的点云执行各种任务,所有这些都基于以下数据结构:
public class Point
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
在任何时候,在各种列表的内存中可能会有数百万个这样的点。其中许多都留在内存中足够长的时间进入垃圾收集的第2代。由于程序以32位模式运行,因此虚拟地址空间有限。
使用此遗留代码的程序有时会与OutOfMemoryExceptions
一起崩溃。即使它们没有崩溃,它们也会消耗比它们应有的更多的内存,并且虚拟地址空间经常被分段到没有大于50mb的连续内存块可用(例如MemoryFailPoint(50)
失败)。有两种方法明确调用GC.Collect()
,删除它们会增加崩溃的频率。
现在,我知道有两种方法可以解决这个问题,我不能使用这两种方法:
使用struct
而不是class
作为积分。不要将这些结构存储在List
中,而是使用arrays
,以避免在每次访问时复制点。结构每个实例的开销远远少于类,并且不会对垃圾收集器造成太多麻烦
不幸的是,这需要对代码进行大量的重大更改;现有方法都期望点类是可变的,并且对各个点的引用在任何地方传递。结构的按值复制语义将导致各种问题。
将整个应用切换为64位。这不会减少内存,但会将虚拟地址空间增加到至少应用程序不再崩溃的程度 不幸的是,有一些传统的32位dll阻止了这种情况。
还有其他方法可以继续使用32位的现有Point类,但是减少内存压力并简化垃圾收集器的工作吗?
我可以在非托管内存中以某种方式分配和释放所有这些点,同时仍然在托管代码中传递引用吗?
还是我错过了另一种解决方法吗?
答案 0 :(得分:1)
在您的评论中,您已经实现了从双字节到浮点从36字节减少到24字节的显着改进:
我用float而不是double进行了一些测试:我可以节省33% (原始的点类是24字节+ 12字节的开销,带有浮点数 这是12字节+ 12字节的开销。)有了一个结构,我可以保存另一个 12个字节的类开销。 – HugoRune16年9月20日在16:19
通过在您的对象中封装一个结构,您可以获得额外的8字节改进,这可以实现每点16字节(浮点数为12字节,开销为4字节),另外节省22%的费用,总计为55% :
public struct PointFloat
{
public float X;
public float Y;
public float Z;
}
public class Point
{
private PointFloat dbls;
public float X
{
get { return dbls.X; }
set { dbls.X = value; }
}
public float Y
{
get { return dbls.Y; }
set { dbls.Y = value; }
}
public float Z
{
get { return dbls.Z; }
set { dbls.Z = value; }
}
}