Проблема (std::bad_alloc) с сохранением большого изображения (около 36 МБ) в БД postgres через QSqlQuery в QThread

У меня проблема с QSqlQuery при попытке сохранить относительно большое (~ 36 МБ) изображение. Когда я вызываю QSqlQuery::exec(), я получаю исключение std::bad_alloc. Когда я работаю с меньшими изображениями, все в порядке. Нужно сказать - этот код выполняется в другом экземпляре QThread, чтобы предотвратить зависание графического интерфейса, весь код скомпилирован в компиляторе MinGW-32, на ПК 8 ГБ ОЗУ. И иногда (очень редко) у меня этот код работает без этого исключения даже с изображениями такого размера (36 МБ)

вот код:

......
QByteArray byte_array_image, byte_array_icon;
QBuffer buffer_image(&byte_array_image), buffer_icon(&byte_array_icon);

buffer_image.open(QIODevice::WriteOnly);
buffer_icon.open(QIODevice::WriteOnly);

image->save(&buffer_image, format.toLocal8Bit().data());
icon->save(&buffer_icon, "JPEG");

query.prepare("INSERT INTO pictures (id, id_act, date_time, name, picture, icon) VALUES (:id, :id_act, :date_time, :name, :picture, :icon)");
......
query.bindValue(":picture", byte_array_image);
query.bindValue(":icon", byte_array_icon);
byte_array_image.clear();
byte_array_icon.clear();

save_succeed = query.exec();

if (!save_succeed)
    emit database_failed(QUERY_ERROR_HEADER, QUERY_ERROR_SAVE_IMAGE + query.lastError().text());

emit query_finished();

return save_succeed; 

Насколько я вижу, проблема может быть связана с нехваткой места в куче для QApplication для создания экземпляра QString, включающего весь байт-код изображения. Чтобы исправить это, я попытался разбить это изображение на десять частей, но это не помогает.

Вот код запроса, разделенный на десять частей:

............
QByteArray byte_array_image, byte_array_icon;
QBuffer buffer_image(&byte_array_image), buffer_icon(&byte_array_icon);

buffer_image.open(QIODevice::WriteOnly);
buffer_icon.open(QIODevice::WriteOnly);

image->save(&buffer_image, format.toLocal8Bit().data());
icon->save(&buffer_icon, "JPEG");

int partlen, parts_count = 0;
save_succeed = false;

while (!save_succeed && ++parts_count < 10)
{
     partlen = byte_array_image.size();
     partlen = byte_array_image.size() / parts_count;

     query.clear();
     query.prepare("INSERT INTO pictures (id, id_act, date_time, name, part_one, parts_count, icon, src, src_act) "
                      "VALUES (:id, :id_act, :date_time, :name, :part_one, :parts_count, :icon, :src, :src_act)");
     ........
     query.bindValue(":part_one", byte_array_image.mid(0, partlen));
     query.bindValue(":parts_count", parts_count);
     query.bindValue(":icon", byte_array_icon);

     try
     {
         save_succeed = query.exec();
     }
     catch (std::bad_alloc &error)
     {
         if (parts_count < 10)
             continue;

         emit database_failed(QUERY_HEAP_ERROR_HEADER, QUERY_HEAP_ERROR_TEXT);

         return false;
     }

 }

 if (save_succeed)
 {
    QStringList part_names = { "part_one", "part_two", "part_three", "part_four", "part_five",
                                   "part_six", "part_seven", "part_eight", "part_nine", "part_ten"};

    for (int current_part = 1; current_part < parts_count; ++current_part)
    {
        query.clear();

        query.prepare("UPDATE pictures SET "+part_names[current_part]+" = :current_part WHERE id = :id AND src = :src");
        query.bindValue(":current_part", current_part == parts_count - 1 ? byte_array_image.mid(current_part*partlen) : byte_array_image.mid(current_part*partlen, partlen));
        ........
        save_succeed = query.exec();
    }
 }

 byte_array_image.clear();
 byte_array_icon.clear();

Я прав с источником проблемы? Могу ли я что-нибудь сделать, чтобы это исправить? 36 мб для изображения не так уж и много.


person Hephaestsus    schedule 23.08.2019    source источник
comment
36 МБ для изображения. Это должно быть 36 МБ непрерывной свободной памяти, а не только 36 МБ свободной памяти. -- На ПК 8 ГБ оперативной памяти -- Это абсолютно ничего не значит, если ваша программа 32-битная. Только 64-битные программы что-то получат от этой памяти.   -  person PaulMcKenzie    schedule 23.08.2019
comment
Да, я понимаю. Могу ли я что-нибудь сделать, чтобы получить 36 МБ непрерывной свободной памяти? Кроме перехода на 64-битную? Более того, я попытался разбить этот запрос на десять частей, поэтому этот код потребовал бы всего ~4 МБ свободной непрерывной памяти. Почему мне это ничего не дало?   -  person Hephaestsus    schedule 23.08.2019
comment
Я не знаю, что query.exec() делает внутри. Ты? Если нет, то вы не можете контролировать то, что выделяется. Во-вторых, std::bad_alloc также выбрасывается, когда для выделения дается недопустимое (то есть отрицательное) число. Кроме того, вместо 10 частей, как насчет 100 частей? Или 1000 деталей? Выясните, действительно ли это связано с памятью или это просто ошибка в вашем коде.   -  person PaulMcKenzie    schedule 23.08.2019
comment
Код, который работает иногда, но не работает где-то, сильно намекает на неопределенное поведение. Тот факт, что вы упоминаете об использовании потоков, только усиливает это подозрение.   -  person G.M.    schedule 24.08.2019


Ответы (1)


1) Попробуйте отредактировать свой postgresql.conf - это может быть ограничение памяти базы данных, а не вашего приложения. Есть небольшие значения по умолчанию, около 20 МБ общих буферов. Используйте эту ссылку в качестве примера https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server

2) Чтобы проверить, является ли это проблемой из базы данных или вашей, перед exec попробуйте выделить и освободить большой буфер памяти - в несколько раз больше, чем запрошенный образ. Если все в порядке и exec выдает исключение памяти, то проблема в базе данных или драйвере, а не в вашем приложении.

3) Также вы можете войти в этот exec под отладчиком и посмотреть, где это bad_alloc происходит

person Konstantine Kozachuck    schedule 23.08.2019
comment
Да, я попробую это. Плохо, иногда этому коду удается сохранять большие изображения в БД без std::bad_alloc, так что я боюсь, что это не так. - person Hephaestsus; 23.08.2019