Как была выделена и освобождена память?

#.net #memory #garbage-collection #clr #idisposable

#.net #память #сборка мусора #среда clr #idisposable

Вопрос:

Я играю с IDisposable interface и GC, и есть кое-что, чего я не могу понять.

Итак, у нас есть следующий класс:

 public class DatabaseState : IDisposable
{
    protected SqlConnection _connection;

    public virtual string GetDate()
    {
        if (_disposed)
            throw new ObjectDisposedException("DatabaseState");

        if (_connection == null)
        {
            var connectionString = ConfigurationManager.ConnectionStrings["master"];
            _connection = new SqlConnection(connectionString.ConnectionString);
            _connection.Open();
        }
        using (var command = _connection.CreateCommand())
        {
            command.CommandText = "SELECT getdate()";
            return command.ExecuteScalar().ToString();
        }
    }

    private bool _disposed;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            if (_connection != null)
            {
                _connection.Dispose();
                _connection = null;
            }
            _disposed = true;
        }
    }
}
  

Как вы можете видеть, класс довольно прост (он получает только текущую дату).

В основном у меня есть следующий код:

 class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("'g' to Get Date; 'gc' to Garbage Collect; 'x' to Exit");
        var command = "";
        while (command != "x")
        {
            command = Console.ReadLine();
            switch (command)
            {
                case "g":
                    GetDate();
                    break;

                case "gc":
                    GC.Collect();
                    break;
            }
        }
    }

    private static void GetDate()
    {
        using (var databaseState = new DatabaseState())
        //var databaseState = new DatabaseState();
        {
            Console.WriteLine(databaseState.GetDate());
        }
    }
}
  

Первый эксперимент заключается в том, что я использую IDisposable с использованием (я использовал VS build в диагностических инструментах.)

Посмотрите на следующие снимки:

Промежутки

Число представляет идентификатор моментального снимка.

  1. Когда я запустил приложение. Как вы можете видеть, мы выделили только 29,38 КБ.
  2. Когда я ввел «g», мы выделили 245,70 КБ, что является нормальным. Мы открыли соединение, квитирование и т.д.
  3. Когда я ввел «g» во второй раз, мы выделили всего 0,55 КБ, что также должно быть нормальным. Я проверил, какой тип объекта мы создали: Hashtable, Microsoft.Win32.SafeHandles.SafeFileMappingHandle,SectionRecord и т.д. Я не знаком с этими типами объектов и думаю, что они каким-то образом связаны с CLR.

С этого момента со мной происходили странные вещи.

4, 5,6,7 Я сделал то же самое, я нажал «g». Как вы можете видеть, мы не выделили никакой памяти. И это мой первый вопрос, почему?.

  1. Я нажал «gc», который вызывает GC.Collect(). В этот момент я был удивлен. Мы не освободили никакой памяти. Насколько я знаю GC.Collect() освободил память от всех поколений. Второй вопрос Почему GC на данный момент не освободил никакой памяти?

  2. Я снова нажал «g». То же самое, что и на снимках 4, 5, 6 и 7. Мы не выделили никакой памяти. Почему?

10, 11 Случайно созданные моментальные снимки.

  1. Это тоже кое-что интересное. Я не выполнял никаких действий, и что-то выделило 2,4 КБ памяти. Я снова проверил, какие типы объектов мы создали: ContextCallback, Microsoft.Win32.SafeHandles.SafeFileMappingHandle, AppDomainPauseManager, ThreadPoolWorkQueue. И снова я не знаком с этими типами, но я думаю, что они связаны с CLR.

  2. То же, что и 12. Я некоторое время не выполнял никаких действий, но на этот раз мы освободили 20,66 КБ памяти. Почему?

Подводя итог:

Насколько я понимаю, IDisposable и GC должны работать немного по-другому. В первый раз, когда я создаю экземпляр DatabaseState, я должен открыть соединение, квитирование и т.д. Из-за этого мы выделим больше памяти. Из-за того факта, что мы используем экземпляр DatabaseState в инструкции using, мы освободим эти неуправляемые ресурсы. В следующий раз, когда я буду использовать экземпляр DatabaseState, я должен выделить немного памяти (но, как я видел на снимках 4,5,6,7 и 9, я не выделял никакой памяти) только для экземпляра, потому что соединение, квитирование уже выполнено.

Мне чего-то не хватает, но я не знаю, чего и где.

С уважением

Комментарии:

1. Если я не ошибаюсь, существует внутренний кэш Sqlconnections. По сути, только первый вызов будет выполнять дорогостоящие действия. Повторное использование объектов довольно распространено для решения проблем gc.

2. Из IDisposable : «Сборщик мусора автоматически освобождает память, выделенную управляемому объекту, когда этот объект больше не используется. Однако невозможно предсказать, когда произойдет сборка мусора.» Обратите внимание на второе предложение. То, что вы, скорее всего, видите, это . Сеть придерживает память, потому что думает, что она может понадобиться вам снова в ближайшем будущем. Если системе нужна память, то . Net вернет все, что сможет, обратно в ОС. Это обычное поведение для любого . Сетевая программа…

3. @Idle_Mind имеет смысл. Знаете ли вы какие-нибудь статьи / книги, где я могу подробно прочитать, что на самом деле происходит?

4. Конечно, в сборке мусора есть ссылки на статьи с более подробной информацией, чем вам, вероятно, когда-либо понадобится.