C# 之 GC 优化与函数方法的联系

减少方法到委托的隐式转换

避免频繁直接将方法当作参数直接传入,因为每次调用时都会产生一个委托实例,应该将方法缓存起来:

private Func<object> func;
private int a;
[SetUp]
public void SetUp()
{
    func = Func;
}
    
[Test]
public void MemberMethodAllocationTest()
{
    Assert.That(() =>
    {
        //有 Allocated
        //编译后:FuncInvoke(new Func<object>(Func));
        FuncInvoke(Func);
    }, Is.AllocatingGCMemory());
}

[Test]
public void VariableAllocationTest()
{
    Assert.That(() =>
    {
        //无 GC
        FuncInvoke(func);
    }, Is.AllocatingGCMemory());
}
private object Func() => null;
private object FuncInvoke(Func<object> func) => func.Invoke();

减少函数闭包

Lambda 表达式的调用,MSBuild 编译器会分三种情况处理:

  1. 若捕获到类成员(包括成员方法),则生成一个本成员的方法,并创建一个该方法的委托实例;
  2. 若捕获到方法内的局部变量,则生成一个内部类和内部类的一个成员方法,并创建该内部类和委托的实例;
  3. 若没有捕获任何变量,则生成一个内部类和内部类的一个静态方法,并创建该内部类,如果是第一次调用,则 new 一个委托并缓存;

我们要避免的是第一种和第二种情况:

public void BadMethod_1()
{
    int a = 5;
    // 捕获局部变量,会 new 一个内部类的实例对象和一个委托对象
    Func<int> func = () => a;
    func.Invoke()
}

int b = 5;
public void BadMethod_2()
{
    // 捕获成员变量,会 new 一个本类的一个委托对象
    Func<int> func = () => b;
    func.Invoke()
}

只要是任何涉及到闭包调用的函数,都会在函数开头插入一个创建内部类的逻辑,也就是说,就算你没有调用到真正的闭包代码,也是会产生 GC Allocated:

public class TestClass
{
    public void GCTest()
    {
        var obj = new object();
        // 本次调用会产生 GC堆 的 Allocated
        Test(true, obj);
    }
    
    public void Test(bool isReturn,object source)
    {
        if(isReturn)
            return;
        // 捕获函数参数
        Select(item => Get(source));
    }
    
    public void Select(Func<object, object> func) { }
    public object Get(object obj) => null;
}

上面的代码编译后:

public class TestClass
{
    //编译器生成的内部类
    [CompilerGenerated]
    private sealed class <>c__DisplayClass1_0
    {
        [System.Runtime.CompilerServices.Nullable(0)]
        public TestClass <>4__this;
        
        [System.Runtime.CompilerServices.Nullable(0)]
        public object source;
        
        internal object <Test>b__0(object item)
        {
            return <>4__this.Get(source);
        }
    }
    
    public void GCTest()
    {
        object obj = new object();
        Test(true, obj);
    }
    
    public void Test(bool isReturn, object source)
    {
        //无论如何它都会创建一个内部类实例,即使你直接 Return
        <>c__DisplayClass1_0 <>c__DisplayClass1_ = new <>c__DisplayClass1_0();
        <>c__DisplayClass1_.<>4__this = this;
        <>c__DisplayClass1_.source = source;
        if (!isReturn)
        {
            Select(new Func<object, object>(<>c__DisplayClass1_.<Test>b__0));
        }
    }
    
    public void Select(Func<object, object> func) {}
    public object Get(object obj) => null;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇