C# 使用许多小型类减少内存压力

C# 使用许多小型类减少内存压力,c#,.net,memory,memory-management,garbage-collection,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位模式运行,虚拟地址空

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

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
    作为点。不要将这些结构存储在
    列表
    中,而是使用
    数组
    ,以避免每次访问时复制点。结构的每个实例的开销比类小得多,并且不会给垃圾收集器带来太多麻烦。
    不幸的是,这需要对代码进行巨大的修改;现有的方法都期望point类是可变的,对各个点的引用到处传递。结构的按值复制语义将导致各种问题

  • 将整个应用程序切换到64位。这不会减少内存,但会增加虚拟地址空间,至少应用程序不会再崩溃。
    不幸的是,有一些传统的32位DLL阻止了这种情况

是否有其他方法可以继续使用32位的现有Point类,但可以减少内存压力并简化垃圾收集器的工作

我能自己在非托管内存中分配和释放所有这些点,同时仍然在托管代码中传递引用吗


或者我还遗漏了其他解决方法吗?

在您的评论中,您已经通过从双字节变为浮点,从36字节显著提高到24字节:


我用float而不是double做了一些测试:我能节省33% (原始point类的开销为24字节+12字节,带有浮点数。) 这是12字节+12字节的开销。)使用一个结构,我可以保存另一个 12字节的类开销HugoRune 2016年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; }
    }
}

“在任何时候,数百万个这样的点可能会在内存中挂起”-所有这些点在那个时候都是必需的并且都被使用了吗?解决方案不是让所有的东西都一直加载到内存中,而是基于当前操作加载/卸载数据块。显然,这将引入一些开销,导致性能下降。您还可以创建点的“池”,并反复重用相同的实例。当然,这取决于当前形势有多糟糕。如果点在这里和那里被传递,并且没有一点(时间上)你知道给定的一堆点不再被使用-那将不起作用。但是你说你想自己“分配和释放”这些点,而“池”点可以做到这一点(不完全一样,但想法是一样的)。Struct会减少内存,但可能没有帮助,因为不是整个对象都在列表中,而不仅仅是一个引用。你真的需要double吗?我用float而不是double做了一些测试:我能够节省33%(原始point类是24字节+12字节的开销,使用float它是12字节+12字节的开销)。使用struct,我可以为类开销再节省12字节。关于池的想法:我无法得到任何改进,跟踪不再需要的对象太复杂了。我认为如果我有一些非常大的物体,而不是很多小物体,游泳池会更合适。