Command.Prepare () вызывает утечку памяти? - PullRequest
2 голосов
/ 21 августа 2010

Я унаследовал некоторый код для этого проекта научного моделирования, и мы с коллегами находимся в тупике из-за этой проблемы.Парня, который написал это, уже нет, поэтому мы не можем его спросить (пойди разберись).

Внутри уровня доступа к данным есть метод insert().Это делает то, на что это похоже - это вставляет записи в базу данных.Он используется различными моделируемыми объектами, чтобы сообщить базе данных о себе во время моделирования.

Однако мы заметили, что при более длительных симуляциях после значительного числа вставок в базу данных мы в конечном итоге получили тайм-ауты соединения.Таким образом, мы увеличили пределы тайм-аута, а затем начали получать ошибки «нехватки памяти» от PostgreSQL.Мы в конечном итоге выявили проблему в строке, где объект IDbCommand использует Prepare().Если оставить его, использование памяти будет бесконечно увеличиваться.Комментирование этой строки приводит к тому, что код работает нормально, и устраняет все проблемы с памятью.Что Prepare() делает, что вызывает это?Я ничего не могу найти в документации, чтобы объяснить это.

Ниже приведена сжатая версия кода.

public virtual void insert(DomainObjects.EntityObject obj)
{
    lock (DataBaseProvider.DataBase.Connection)
    {
        IDbCommand cmd = null; 
        IDataReader noInsertIdReader = null;
        IDataReader reader= null;

        try
        { 
            if (DataBaseProvider.DataBase.Validate)
            {    ...    }

            // create and prepare the insert command
            cmd = createQuery(".toInsert", obj);
            cmd.Prepare();     // This is what is screwing things up

            // get the query to retreive the sequence number
            SqlStatement lastInsertIdSql = DAOLayer...getStatement(this.GetType().ToString() + ".toGetLastInsertId");

            // if the obj insert does not use a sequence, execute the insert command and return
            if (lastInsertIdSql == null)
            {
                noInsertIdReader = cmd.ExecuteReader();
                noInsertIdReader.Close();
                return;
            }

            // append the sequence query to the end of the insert statement
            cmd.CommandText += ";" + lastInsertIdSql.Statement;

            reader = cmd.ExecuteReader();

            // read the sequence number and set the objects id
            ...
        }

        // deal with some specific exceptions
        ...
    }
}

РЕДАКТИРОВАТЬ: (в ответ на первый ответ) Вся база данныхобъекты располагаются в блоке finally.Я просто вырезал эту часть здесь, чтобы сэкономить место.Мы немного поиграли с этим, и это не имело никакого значения, поэтому я не думаю, что это проблема.

Ответы [ 2 ]

3 голосов
/ 21 августа 2010

Вы заметите, что IDbCommand и IDataReader реализуют IDisposable . Всякий раз, когда вы создаете экземпляр объекта IDisposable, вы должны либо обернуть его в , используя оператор , либо вызвать Dispose , как только вы закончите. Если вы этого не сделаете, вы в конечном итоге утечки ресурсов (иногда ресурсы, отличные от просто памяти).

Попробуйте это в своем коде

using (IDbCommand cmd = createQuery(".toInsert", obj))
{
    cmd.Prepare();     // This is what is screwing things up
    ...
    //the rest of your example code
    ...
}

РЕДАКТИРОВАТЬ, чтобы говорить конкретно о подготовке

Я могу видеть из кода, что вы готовите команду, а затем никогда не используете ее повторно.

Идея, лежащая в основе подготовки команды, заключается в том, что подготовка требует дополнительных затрат, но каждый раз, когда вы используете команду, она будет более эффективной, чем не подготовленная инструкция. Это хорошо, если у вас есть команда, которую вы собираетесь многократно использовать, и это компромисс того, стоит ли накладных расходов увеличение производительности команды.

Таким образом, в коде, который вы нам показали, вы готовите команду (оплачиваете все накладные расходы) и не получаете никакой выгоды, потому что вы сразу же отбрасываете команду!

Я бы либо перезапустил подготовленную команду, либо просто отменил вызов на оператор подготовки.

Понятия не имею, почему подготовленные команды просачиваются, но вам не нужно готовить так много команд, особенно команд одноразового использования.

2 голосов
/ 21 августа 2010

Метод Prepare () был разработан для обеспечения более эффективного выполнения запроса.Это полностью зависит от поставщика, чтобы реализовать это.Типичная процедура создает временную хранимую процедуру, предоставляя серверу возможность предварительного анализа и оптимизации запроса.

Существует несколько способов, с помощью которых подобный код может привести к утечке памяти.Одним из них является типичная деталь .NET, практическая реализация класса IDbCommand всегда имеет метод Dispose () для явного освобождения ресурсов до того, как это сделает поток финализатора.Я не вижу его в вашем фрагменте.Но в этом случае маловероятно, что очень сложно использовать всю память, даже не запустив сборщик мусора.Вы можете узнать из Perfmon.exe и наблюдать за счетчиками производительности для сборщика мусора.

Следующий кандидат более коварен, вы используете большой фрагмент собственного кода.Поставщики Dbase не так просты.Тип FOSS, как правило, предназначен для того, чтобы вы могли избавиться от них.Исходный код доступен по причине.Perfmon.exe снова, чтобы диагностировать это, видя, что управляемые кучи не выходят за границы, а взрываются частные байты - это пустяк.

Если вам не очень нравится отлаживать провайдера, вы можете просто прокомментировать утверждение.

...