Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closure allocations in C#

I've installed the Clr Heap Allocation Analyzer extension and in a project I see something that I quite don't understand, I've got a method with a signature

public Task<int> ExecuteAsync(string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{
    param = SetModificationValuesForGlobalRing(param);
    return _sqlPolicy.ExecuteAsync(async () =>
    {
        int result;
        using (var connection = new SqlConnection(_connectionString))
        {
            await connection.OpenAsync();
            result = await connection.ExecuteAsync(sql, param as object, transaction, commandTimeout, commandType);
        }
        return result;
    });
}

This tools' giving me a warning on the method and all the parameters that says

The compiler will emit a class that will hold this as a field to allow capturing of this closure.

I don't know why this behaviour happens, is it due to the optional parameters?

like image 637
evilpilaf Avatar asked Mar 28 '16 21:03

evilpilaf


1 Answers

You must be:

  1. Calling this code from within an anonymous function, like a lambda.

    • or -
  2. Using the yield/await keyword somewhere.

When you use either of the above, C# must create a closure to capture any variables used within the closure that have an originating scope outside of your lambda (in the first case) or usage before and after the yield/await (in the second case).

C# captures the closure by creating an anonymous class -- defined at compile-time -- in memory. It then creates a field on that anonymous class for each piece of information it needs to persist. This can result in an object instance's lifetime being extended for the life of the closure or the life of the method with the yield/await.

Sometimes the lifetime is longer than had you NOT used yield/await or the lambda. In that case, you might notice memory usage that is higher than you expected (as the garbage collector won't collect the object instance until the closure is completely out of scope or the method containing the yield/await has completed).

The warning you see is simply your tool trying to explain the above to you, so that you know to expect this behavior and the resulting potential increase in memory usage.

like image 113
Ayo I Avatar answered Nov 16 '22 00:11

Ayo I