0

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

我有两种方法,第一个创建 5000 个对象而不使用该模式,另一个创建 5000 个对象使用该模式。在他们每个人创建对象之后,他们都会调用一个方法,该方法返回 App 使用的当前内存。

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();
    }
}

我像使用 Parts 列表的类一样使用该模式,如果存在则返回一个 part 对象。

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 类中):

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) 的控制器中的方法时,返回(current)+2mb,但是当我调用 具有模式(/weatherforecast/GenerateLiftMemory) 的方法时,返回(当前)+3mb

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

带有测试代码的存储库。Gitlab存储库内存api

4

1 回答 1

1

使用的代码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;
    }
}

及其结果

方法 意思是 错误 标准差 0代 第一代 第 2 代 已分配
简单树 9.040 毫秒 0.1804 毫秒 0.2346 毫秒 718.7500 453.1250 265.6250 4.44 MB
飞树 19.701 毫秒 0.1716 毫秒 0.1521 毫秒 2500.0000 906.2500 437.5000 15.88 MB
FlyTreesImproved 18.075 毫秒 0.2869 毫秒 0.2684 毫秒 1781.2500 625.0000 312.5000 10.92 MB
FlyTreesWithoutLambda 4.919 毫秒 0.0273 毫秒 0.0242 毫秒 421.8750 281.2500 281.2500 2.53 MB
于 2021-05-19T09:44:28.230 回答