减少类似对象的记忆

时间:2009-11-13 06:30:30

标签: c# design-patterns performance

我正在考虑减少像集合对象这样的表的内存消耗。

给出类似

的类结构
Class Cell
{
    public property int Data;
    public property string Format;
}

Class Table
{
    public property Dictionary<Position, Cell> Cells;
}

当存在大量单元格时,Cell类的Data属性可以是可变的,但Format属性可以重复多次,例如,标题单元格可以具有标题的空格式字符串,数据单元格可以都是“0.00”。

一个想法是类似于以下内容

Class Cell
{
    public property int Data;
    public property int FormatId;
}
Class Table
{
    public property Dictionary<Position, Cell> Cells;
    private property Dictionary<Position, string> Formats;

    public string GetCellFormat(Position);
}

这会节省字符串上的内存,但FormatId整数值仍会重复多次。

有没有比这更好的实施?我看过flyweight模式,但我不确定它是否与此匹配。

我正在考虑的一个更复杂的实现是从Cell类中完全删除Format属性,而是将格式存储在将相邻单元格组合在一起的字典中 例如可能有2个这样的条目
    <item rowFrom=1 rowTo=1 format="" /&GT;
    <item romFrom=2 rowTo=1000 format="0.00" /&GT;

3 个答案:

答案 0 :(得分:5)

对于弦乐,你或许可以看看实习生;或者使用内置的interner,或者(最好)自定义interner - 基本上是Dictionary<string,string>。这意味着每个相同的字符串使用相同的引用 - 并且可以收集重复项。

不要对int做任何事情;这已经是最佳的了。

例如:

using System;
using System.Collections.Generic;
class StringInterner {
    private readonly Dictionary<string, string> lookup
        = new Dictionary<string, string>();
    public string this[string value] {
        get {
            if(value == null) return null;
            if(value == "") return string.Empty;
            string result;
            lock (lookup) { // remove if not needed to be thread-safe     
                if (!lookup.TryGetValue(value, out result)) {
                    lookup.Add(value, value);
                    result = value;
                }
            }
            return result;
        }
    }
    public void Clear() {
        lock (lookup) { lookup.Clear(); }
    }
}
static class Program {
    static void Main() {
        // this line is to defeat the inbuilt compiler interner
        char[] test = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' };

        string a = new string(test), b = new string(test);
        Console.WriteLine(ReferenceEquals(a, b)); // false
        StringInterner cache = new StringInterner();
        string c = cache[a], d = cache[b];
        Console.WriteLine(ReferenceEquals(c, d)); // true
    }
}

如果需要,您可以使用WeakReference进一步。

请注意,不需要更改设计 - 只需更改填充对象的代码即可使用内部/缓存。

答案 1 :(得分:4)

您是否确实确定这是否确实存在问题? CLR代表你做了很多string interning因此有可能(取决于CLR版本以及编译代码的方式)你没有使用你认为的那么多的内存。

我强烈建议您在更改设计之前验证对内存利用率的怀疑。

答案 2 :(得分:0)

正如其他人所说,在改变你的设计之前,你首先要看看这是否是一个问题。如果这是一个问题和/或你处理大量的稀疏数据,那么稀疏的数据结构可能更适用于问题。我稍后会发布一个非常简单的天真实现(因为我现在还不能)但是二维稀疏矩阵可以做你想要的。

概念是将为给定的单元格范围存储单个格式(字符串或类),例如1-1000。要从中受益,您必须进行一些设计更改...需要从单元类中删除format属性。相反,格式应该在表类中注册,或者最好在另一个类中注册。例如

public class CellFormats 
{ ....
public void Register(int start, int finish, string format);
}

单元格格式类将包含稀疏矩阵,该矩阵将包含范围的格式。

Table类将使用CellFormats类。它没有一个名为GetCellFormat的方法,而是有一个带有以下签名的方法

void ApplyCellFormat(Position cellPosition)

这将从CellFormats类(稀疏矩阵)中检索单元格格式并将其应用于单元格。

如果数据很常见并应用于大范围,此技术可以大大减少内存使用量。但正如我已经说过的那样,在通过添加这样的代码使设计更复杂之前,你需要确保这是问题的原因。

相关问题