这个简单的网络核心 Api 中的享元模式使用了更多的内存 ram

Flyweight pattern in this simple net core Api uses more memory ram

我正在尝试在一个简单的 .net 核心中应用 Flyweight 方法模式 Api 以查看与不使用该模式相比节省了多少内存。

我有两种方法,第一种不使用模式创建 5000 个对象,另一种使用模式创建 5000 个对象。在他们每个人创建对象之后,然后他们调用一个方法,该方法return是应用程序使用的当前内存。

public class MemoryService : IMemoryService
{
    private readonly TreeFactory _treeFactory;
    public MemoryService()
    {
        _treeFactory = new TreeFactory();
    }

    //create without pattern
    public long SetObjectsMemory()
    {
        List<Tree> trees = new List<Tree>();
        for (int i = 0; i < 5000; i++)
        {
            var tree = new Tree()
            {
                Id = new Random().Next(1, 9999999),
                Part = new PartTree()
                {
                    Name = "Nameany",
                    Bark = "Barkany",
                    Color = "Colorany"
                }
            };
            trees.Add(tree);
        };

        return Utilities.GetCurrentMemoryUsed();
    }

    //crete with flyweight pattern
    public long SetObjectsMemoryFactory()
    {
        List<Tree> trees = new List<Tree>();
        for (int i = 0; i < 5000; i++)
        {
            var tree = new Tree()
            {
                Id = new Random().Next(1, 9999999),
                Part = _treeFactory.GetPartTree("Nameany", "Barkany", "Colorany")
            };
            trees.Add(tree);
        }

        return Utilities.GetCurrentMemoryUsed();
    }
}

我使用类似 class 的模式,它使用部件列表和 return 部件对象(如果存在)。

public class TreeFactory
{
    private static List<PartTree> _parts;

    public TreeFactory() {
        _parts = new List<PartTree>();
    }
    public PartTree GetPartTree(string name, string bark, string color)
    {
        if (_parts.Any(x => x.Name == name && x.Bark == bark && x.Color == color))
        {
            return _parts.Where(x => x.Name == name && x.Bark == bark && x.Color == color).FirstOrDefault();
        }
        else {
            var newpart = new PartTree()
            {
                Name = name,
                Bark = bark,
                Color = color
            };
            _parts.Add(newpart);
            return newpart;
        }
        
    }
}

获取App当前使用内存的方式是使用Process这种方式(在Utilities中class):

public static long GetCurrentMemoryUsed() {
        Int64 memory;
        using (Process proc = Process.GetCurrentProcess())
        {
            memory = proc.PrivateMemorySize64 / (1024 * 1024);
        }

        return memory;
    }

在我的 Startup 中,我像 Singleton 一样注入 MemoryService。在控制器中,我使用 3 种方法调用函数:

 [HttpGet, Route(nameof(WeatherForecastController.GenerateMemory))]
    public IActionResult GenerateMemory()
    {
        var total=_memoryService.SetObjectsMemory();
        return Ok(total);
    }

    [HttpGet, Route(nameof(WeatherForecastController.GenerateLiftMemory))]
    public IActionResult GenerateLiftMemory()
    {
        var total = _memoryService.SetObjectsMemoryFactory();
        return Ok(total);
    }

    [HttpGet, Route(nameof(WeatherForecastController.GetMemory))]
    public IActionResult GetMemory()
    {
        var total = Utilities.GetCurrentMemoryUsed();
        return Ok(total);
    }

问题是:当我在导航器中调用控制器中的方法时 没有模式 (/weatherforecast/GenerateMemory),然后这个 returns (current)+2mb,但是当我调用该方法时 with pattern (/weatherforecast/GenerateLiftMemory) this returns (current)+3mb.

为什么有模式享元 returns 的方法比没有模式的方法使用更多的 MB(增长)??

包含测试代码的存储库。 Gitlab repository memory api

使用 TreeFactory 的代码消耗更多内存,因为它的 GetPartTree 方法在循环中被多次调用,例如 Linq 方法 AnyWhere在里面。这两种方法都会在后台创建额外的 Iterator 对象以遍历集合,这会导致额外的内存消耗。

我使用 BenchmarkDotNet 编写了简单的基准测试,并提供了更多选项来演示该问题

扩展内存服务

public class MemoryService : IMemoryService
{
    private const int TreeCount = 50000;
    private readonly TreeFactory _treeFactory;
    public MemoryService()
    {
        _treeFactory = new TreeFactory();
    }

    //crea objetos en memoria sin patrones
    public decimal SetObjectsMemory()
    {
        List<Tree> trees = new List<Tree>();
        for (int i = 0; i < TreeCount; i++)
        {
            var tree = new Tree()
            {
                Id = 1,
                Part = new PartTree()
                {
                    Name = "Nameany",
                    Bark = "Barkany",
                    Color = "Colorany"
                }
            };
            trees.Add(tree);
        };

        return Utilities.GetCurrentMemoryUsed();
    }

    //crea objetos en memoria usando patron flyweight
    public decimal SetObjectsMemoryFactory()
    {
        List<Tree> trees = new List<Tree>();
        for (int i = 0; i < TreeCount; i++)
        {
            var tree = new Tree()
            {
                Id = 1,
                Part = _treeFactory.GetPartTree("Nameany", "Barkany", "Colorany")
            };
            trees.Add(tree);
        }

        return Utilities.GetCurrentMemoryUsed();
    }

    public decimal SetObjectsMemoryFactoryImproved()
    {
        List<Tree> trees = new List<Tree>();
        for (int i = 0; i < TreeCount; i++)
        {
            var tree = new Tree()
            {
                Id = 1,
                Part = _treeFactory.GetPartTreeImproved("Nameany", "Barkany", "Colorany")
            };
            trees.Add(tree);
        }

        return Utilities.GetCurrentMemoryUsed();
    }

    //crea objetos en memoria usando patron flyweight
    public decimal SetObjectsMemoryFactoryWithoutLambda()
    {
        List<Tree> trees = new List<Tree>();
        for (int i = 0; i < TreeCount; i++)
        {
            var tree = new Tree()
            {
                Id = 1,
                Part = _treeFactory.GetPartTreeWithoutLambda("Nameany", "Barkany", "Colorany")
            };
            trees.Add(tree);
        }

        return Utilities.GetCurrentMemoryUsed();
    }
}

扩展树工厂

public class TreeFactory
{
    private static List<PartTree> _parts;

    public TreeFactory()
    {
        _parts = new List<PartTree>();
    }

    public PartTree GetPartTree(string name, string bark, string color)
    {
        if (_parts.Any(x => x.Name == name && x.Bark == bark && x.Color == color))
        {
            return _parts.Where(x => x.Name == name && x.Bark == bark && x.Color == color).FirstOrDefault();
        }

        var newpart = new PartTree()
        {
            Name = name,
            Bark = bark,
            Color = color
        };
        _parts.Add(newpart);
        return newpart;
    }

    public PartTree GetPartTreeImproved(string name, string bark, string color)
    {
        var existingPart = _parts.Where(x => x.Name == name && x.Bark == bark && x.Color == color).FirstOrDefault();
        if (existingPart != null)
            return existingPart;

        var newpart = new PartTree()
        {
            Name = name,
            Bark = bark,
            Color = color
        };
        _parts.Add(newpart);
        return newpart;

    }

    public PartTree GetPartTreeWithoutLambda(string name, string bark, string color)
    {
        for (int i = 0; i < _parts.Count; i++)
        {
            var x = _parts[i];
            if (x.Name == name && x.Bark == bark && x.Color == color)
                return x;
        }

        var newpart = new PartTree()
        {
            Name = name,
            Bark = bark,
            Color = color
        };
        _parts.Add(newpart);
        return newpart;

    }
}

单独控制台项目中的基准测试

class Program
{
    static void Main(string[] args)
    {
        var result = BenchmarkRunner.Run<MemoryBenchmark>();
    }
}


[MemoryDiagnoser]
public class MemoryBenchmark
{
    private IMemoryService memoryService;

    [GlobalSetup]
    public void Setup()
    {
        memoryService = new MemoryService();
    }

    [Benchmark]
    public object SimpleTrees()
    {
        var trees = memoryService.SetObjectsMemory();
        return trees;
    }

    [Benchmark]
    public object FlyTrees()
    {
        var trees = memoryService.SetObjectsMemoryFactory();
        return trees;
    }

    [Benchmark]
    public object FlyTreesImproved()
    {
        var trees = memoryService.SetObjectsMemoryFactoryImproved();
        return trees;
    }

    [Benchmark]
    public object FlyTreesWithoutLambda()
    {
        var trees = memoryService.SetObjectsMemoryFactoryWithoutLambda();
        return trees;
    }
}

及其结果

Method Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
SimpleTrees 9.040 ms 0.1804 ms 0.2346 ms 718.7500 453.1250 265.6250 4.44 MB
FlyTrees 19.701 ms 0.1716 ms 0.1521 ms 2500.0000 906.2500 437.5000 15.88 MB
FlyTreesImproved 18.075 ms 0.2869 ms 0.2684 ms 1781.2500 625.0000 312.5000 10.92 MB
FlyTreesWithoutLambda 4.919 ms 0.0273 ms 0.0242 ms 421.8750 281.2500 281.2500 2.53 MB