减少许多小班的记忆压力

时间:2016-09-20 10:10:26

标签: c# .net memory memory-management garbage-collection

我在一些遗留代码中遇到内存问题 代码使用巨大的点云执行各种任务,所有这些都基于以下数据结构:

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类,但是减少内存压力并简化垃圾收集器的工作吗?

我可以在非托管内存中以某种方式分配和释放所有这些点,同时仍然在托管代码中传递引用吗?

还是我错过了另一种解决方法吗?

1 个答案:

答案 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; }
    }
}