Russian Qt Forum

Qt => Базы данных => Тема начата: cdsmika от Май 16, 2014, 11:39



Название: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 16, 2014, 11:39
Привет!
Выбираю из MSSQL через QODBC данных на 248 Мб (как показывает Management Studio) в память для обработки и пересыла по фтп.
Все замечательно, пока код в основном потоке. Стоит перенести в отдельный поток - валится bad_alloc.
Собственно интересно почему?

Написал тест:
Код:
void Thread::run()
{
    QSqlDatabase udb;
    udb = QSqlDatabase::addDatabase( "QODBC", "conn" );
    udb.setDatabaseName( "Driver={SQL Server Native Client 10.0};Server=(local);Database=ks;UID=sa;PWD=1;" );
    udb.open();

    QStringList l;

    QSqlQuery query( udb );
    query.setForwardOnly(true);
    query.prepare("EXEC dbo.get_initial_data '56FEB471-37A7-494E-AF87-8FC03174C74F'");
    query.exec();

    while (query.next())
    {
        l.append(query.value(1).toString());
    }
}


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 16, 2014, 11:52
Даже вот так уже bad_alloc:
Код:
void Thread::run()
{
    QString l;

    while (l.length() < 200000000)
    {
        l.append("1111111111111");
    }
}


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 16, 2014, 12:01
Win 7 x64 + Qt 4.8.4


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Hellraiser от Май 16, 2014, 12:36
А ~400 Мб не многовато для стековой переменной?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 16, 2014, 12:40
Тут больше интересно, почему валится, а не то, что многовато  :)
Кода бы увидеть побольше!


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 16, 2014, 12:49
А ~400 Мб не многовато для стековой переменной?
как узнать многовато или нет?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 16, 2014, 12:51
Тут больше интересно, почему валится, а не то, что многовато  :)
Кода бы увидеть побольше!
а это собственно весь код)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 16, 2014, 12:54
Ничего не вылетает. Спокойно завершается поток, потолок потребления памяти примерно 550 мб. Видимо у вас где-то что-то ещё.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: m_ax от Май 16, 2014, 12:57
А ~400 Мб не многовато для стековой переменной?
как узнать многовато или нет?
Это не имеет отношения к делу..

QString - это фактически контейнер, который при расширении (из-за ваших постоянных append ов) пытается аллоцировать себе новый блок памяти.. Видимо вы уже до того её дефрагментировали, что такого цельного куска просто нет, вот он кидает bad_alloc.  


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 16, 2014, 13:02
А ~400 Мб не многовато для стековой переменной?
как узнать многовато или нет?
Это не имеет отношения к делу..

QString - это фактически контейнер, который при расширении (из-за ваших постоянных append ов) пытается аллоцировать себе новый блок памяти.. Видимо вы уже до того её дефрагментировали, что такого цельного куска просто нет, вот он кидает bad_alloc.  

Я пробовал аллоцировать и цельный кусок ) Результат тот же. Как понять тогда влезет кусок или нет?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 16, 2014, 13:06
Определять влезет/не влезет -совсем плохой метод. Проще ловить исключение и реорганизовать работу программы, чтобы не приходилось таскать в памяти такие огромные куски данных.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: m_ax от Май 16, 2014, 13:08
Цитировать
Как понять тогда влезет кусок или нет?
Ну как-как.. Если исключение не кинет, значит влезет, а ели кинет, то значит нет)

Ловите исключения и решайте сами, как на это реагировать..


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 16, 2014, 13:41
Определять влезет/не влезет -совсем плохой метод. Проще ловить исключение и реорганизовать работу программы, чтобы не приходилось таскать в памяти такие огромные куски данных.
По нынешним меркам разве огромные? Да для среднего сервака это ж пшик)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 16, 2014, 14:11
Для сервака, сколько бы "планок" в нем не стояло, требования к производительности и маленькому потреблению памяти еще выше и жестче.
И дело не в нынешних мерках, а в хороших программах. Если руководствоваться при программировании мнением, что "ну на всех нынешних компах памяти больше гига, так что можно смело отхватить пару сотен метров", то это приведет к тому в итоге, что программа будет работать криво, а зачастую вообще не будет работать, если по каким-то причинам памяти ей не хватит свободной. Надо работать с тем, что дает система, а не с тем, что "да все компы мощные уже, везде уверен будет работать"  ;)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 16, 2014, 14:15
Ну тут тоже не совсем верно, OKTA. Опять таки по задаче смотреть и направлению.
Какие то программы должны работать только на больших серверах :)

Ну и нижнюю планку тоже надо держать. Ведь как не удумай, упихнуть средней сложности программу для работы с данными в 16-20 мб вряд ли получится :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 16, 2014, 14:32
Ну так я и не призываю выделять под свои программы 10 мегабайт и ни байта больше  ;D
Я говорю о том, что пользоваться тем надо, что может дать операционная система, на которой работает программа, а не планки, установленные в машину) И то по-возможности стараться минимизировать) Динамическая работа с памятью - залог здоровья программиста и системы  ;D Когда человек совершает покупку, он руководствуется не тем, сколько у него денег накоплено всего, а тем, сколько он может потратить сейчас  :) Ну, может кто-то поступает иначе, но это обычно приводит к плачевным последствиям  :) А в итоге все к тому, что надо просто ловить исключения при выделении памяти  ;D


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 16, 2014, 14:42
Ну так я и не призываю выделять под свои программы 10 мегабайт и ни байта больше  ;D
Я говорю о том, что пользоваться тем надо, что может дать операционная система, на которой работает программа, а не планки, установленные в машину) И то по-возможности стараться минимизировать) Динамическая работа с памятью - залог здоровья программиста и системы  ;D Когда человек совершает покупку, он руководствуется не тем, сколько у него денег накоплено всего, а тем, сколько он может потратить сейчас  :) Ну, может кто-то поступает иначе, но это обычно приводит к плачевным последствиям  :) А в итоге все к тому, что надо просто ловить исключения при выделении памяти  ;D
Как определить то это магическое число, которое можно юзать? Откуда мне знать 10 метров или 100? Наобум? Или как предлагают ехепшонами опытным путем? Будет ли гарантия, что на всех компах оно одинаково?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 16, 2014, 14:53
Вот так (http://www.cplusplus.com/reference/new/bad_alloc/):
Код:
 try
  {
    int *myarray= new int[10000];
  }
  catch (std::bad_alloc& ba)
  {
    std::cerr << "bad_alloc caught: " << ba.what() << '\n';
  }
Ну и соответственно так же можно "затрайкэтчить" append.
Оно одинаково это что?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Johnik от Май 17, 2014, 01:38
А ~400 Мб не многовато для стековой переменной?
как узнать многовато или нет?
В винде для потока (thread) по умолчанию для стека выделяется 1 Мб памяти.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 17, 2014, 08:15
Не надо таких сравнений :) У нас же QThread :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Johnik от Май 17, 2014, 14:45
Не надо таких сравнений :) У нас же QThread :)
На счет сравнений это мне?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 17, 2014, 14:49
Угум. Тем более что QThread не имеет таких явных ограничений.

PS я не особо разбираюсь в тонкостях этого вопроса, но 1Гб данных на стеке держал спокойно :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 17, 2014, 14:54
Угум. Тем более что QThread не имеет таких явных ограничений.
QThread это просто класс врапер на системными нитками. Под Вендой он создаёт обычную вендовую нить, со всеми её ограничениями.

PS я не особо разбираюсь в тонкостях этого вопроса
Это видно.

но 1Гб данных на стеке держал спокойно :)
Не придумывайте.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 17, 2014, 14:58
Таки да, попутал названия :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Johnik от Май 17, 2014, 14:59
Угум. Тем более что QThread не имеет таких явных ограничений.
Первое, у ТС Windows, об этом он сообщал. Второе, QThread в Windows обертка над виндовым потоком.
В документации к QThread сообщается:
Цитировать
the maximum stack size is automatically determined by the operating system.
Теперь топаем в msdn (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686774(v=vs.85).aspx) и видим, что по умолчанию размер стека равен 1 mb. И там же читаем как это можно изменить.

PS я не особо разбираюсь в тонкостях этого вопроса
Так разберитесь.

но 1Гб данных на стеке держал спокойно :)
пример проекта "в студию"


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 17, 2014, 15:10
Johnik - да, я ошибся (повторюсь специально для вас) :) Терминология у меня хромает ^.^
Собственно я уже почитал, попробовал и признал свою ошибку :D



Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 09:19
А при чем здесь стэк в конце концов? Во время append используется не стэк, а динамическая память же  ???


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 19, 2014, 10:16
Я честно не понимаю в чём проблема :)
Судя по опыту - всё работает нормально и в штатном режиме.
Есть только пара вариантов:
1) программа к моменту выделения памяти уже заняла более 1,3 Гб памяти (+525 мб + погрешность и >2 Гб и вылет).
2) Где то бьётся память в программе.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 10:47
А у меня при запуске этого теста из креатора сжирается около 500 метров и помирает  ;D


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 19, 2014, 10:51
А у меня при запуске этого теста из креатора сжирается около 500 метров и помирает  ;D
Для 32 битных платформ это нормально.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 10:52
а у меня 64-битная  ;D


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 19, 2014, 10:54
а у меня 64-битная  ;D
Венда?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 10:55
Yes


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 19, 2014, 10:57
А Qt 32 битный? :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 11:01
раскусил  ;D


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 19, 2014, 11:03
Соберите сами 64 битный и попробуйте воспроизвести на нем. :)
 


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 19, 2014, 11:47
Эммм... как умирает?

Быть просто не может смерти при динамическом выделении 500 мб памяти. (если есть хотя бы гиг, а лучше больше памяти в компе)

Разрабатывал одно приложение, оно жрало до 1,8 Гб памяти. Windows XPx32/Windows 7x32/Windows 7x64. Ни разу не было умираний... Мб в креаторе дело?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 11:53
Вероятно)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 19, 2014, 12:09
Быть просто не может смерти при динамическом выделении 500 мб памяти. (если есть хотя бы гиг, а лучше больше памяти в компе)
Совершенно не важно сколько физической памяти в машине (хоть 100500 Гб), все упирается в 32 битное адресное пространство.
И умирать будет из-за невозможности выделения непрерывного куска памяти. Но m_ax в начале темы все уже написал.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 19, 2014, 12:14
100500 Гб не надо. Достаточно 2 Гб, отведённых под x32 разрядный процесс.
И 500 Мб неразрывного участка памяти - это пшик для процесса, не занятого другой деятельностью.
И как говорит ТС - у него падает на простом Thread/String/Append.

PS поставлю вопрос иначе - как можно дефрагментировать память - надёжно. Чтобы не осталось 500 мб куска в этих 2 Гб?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 19, 2014, 12:17
PS поставлю вопрос иначе - как можно дефрагментировать память - надёжно. Чтобы не осталось 500 мб куска в этих 2 Гб?
Как раз вот таким тестом, который использовал ОКТА.
Вместо QString может быть что угодно (чему нужны непрерывные куски памяти): QVector, QByteArray, ...


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 12:18
В каждом аппенде выделяется по 10 байт - тут проблема не в непрерывном участке памяти..


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 19, 2014, 12:20
В каждом аппенде выделяется по 10 байт - тут проблема не в непрерывном участке памяти..
Именно в нем.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Пантер от Май 19, 2014, 12:33
Сам столкнулся с такой проблемой. В проге приходится периодически создавать/удалять несколько экземпляров структурки с размером 16 метров (это сторонний код). Так вот, через пару дней работы программы она падает с бэд аллок - память фрагментируется на столько, что нет свободного непрерывного блока. Потребление памяти в это время меньше 200 метров.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Bepec от Май 19, 2014, 12:42
Таки интересно, почему у меня падает только при 300кк циклов. Вопрос - как с этим бороться?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Май 19, 2014, 12:44
Вопрос - как с этим бороться?
Например, использовать преаллокацию или другие структуры хранения данных в памяти.
Или перейти на 64 битные платформы, там такой проблемы не возникнет еще долго. :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 12:50
Заглянул в исходники QString... Ну да, видимо косяк в непрерывном участке.. Как я понял, QString у меня сжирает 500 метров памяти, дальше пытается сделать realloc, выделив новые 500 метров + еще сколько-то, чего не получается и выкидывает исключение..




Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Пантер от Май 19, 2014, 12:51
Пробовал Кьют собрать под 64 мингвом - сходу не получилось. :(


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 14:34
Эммм... как умирает?

Быть просто не может смерти при динамическом выделении 500 мб памяти. (если есть хотя бы гиг, а лучше больше памяти в компе)

Разрабатывал одно приложение, оно жрало до 1,8 Гб памяти. Windows XPx32/Windows 7x32/Windows 7x64. Ни разу не было умираний... Мб в креаторе дело?

Мой тест код всего навсего:
Код:
void tst_FtpWorker::initTestCase()
{
    th.setStackSize(1000000000);
    th.start();
    th.wait();
}

Код:
void Thread::run()
{
    QString l;

    while (l.length() < 200000000)
    {
        l.append("1111111111111");
    }
}
Падает (см. приложение)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 14:37
А если выполнить в основном потоке
Код:
void tst_FtpWorker::initTestCase()
{
    QString l;

    while (l.length() < 200000000)
    {
        l.append("1111111111111");
    }
}
то все ок (см приложение) и выделенной памяти меньше в два раза


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Пантер от Май 19, 2014, 14:39
Пиплы, а при чем тут стек? QString хранит данные в куче. Это же ПИМПЛ.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 15:49
Стэк он случайно прицепился и ни как не отцепляется  ;D


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 16:03
Короче поставил я setStackSize(10000) и заработало также как и в основном потоке, а вот с SQLQuery такое не прокатывает. Теперь падает с bad_alloc и в основном потоке:
Код:
void tst_FtpWorker::initTestCase()
{
    QSqlDatabase udb;
    udb = QSqlDatabase::addDatabase( "QODBC", "conn" );
    udb.setDatabaseName( "Driver={SQL Server Native Client 10.0};Server=(local);Database=ks;UID=sa;PWD=1;" );
    udb.open();

    QStringList l;

    QSqlQuery query( udb );
    query.setForwardOnly(true);
    query.prepare("EXEC dbo.get_initial_data '56FEB471-37A7-494E-AF87-8FC03174C74F'");
    query.exec();

    while (query.next())
    {
        l.append(query.value(1).toString());
    }
}
Повторюсь, что квэрятина возвращает ок 248 метров данных


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 16:25
ради интереса, поставь вместо append что-нибудь типа new char[] с размером того блока, который получаешь из query.
И вопрос по исключениям в тему.. Почему не ловится исключение?? Просто вылетает с сообщением о std::bad_alloc..

Код:
int main(int argc, char *argv[])
{
    while (1) {
        //

        try {
            //

            new char;

        } catch(std::bad_alloc) {
            //

            qDebug() << "BAD ALLOC";
            break;
        }
    }
    return 0;
}


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 16:46
ради интереса, поставь вместо append что-нибудь типа new char[] с размером того блока, который получаешь из query.
И вопрос по исключениям в тему.. Почему не ловится исключение?? Просто вылетает с сообщением о std::bad_alloc..

Код:
int main(int argc, char *argv[])
{
    while (1) {
        //

        try {
            //

            new char;

        } catch(std::bad_alloc) {
            //

            qDebug() << "BAD ALLOC";
            break;
        }
    }
    return 0;
}

Будет все ок. Выяснил. что вылетает именно QSqlQuery::value(int)
Попробую порезать данные на части и работать через файло. Все равно через фтп передавать. Но вот для других задач вопрос что делать. Ведь через файло не везде удобно


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 16:54
а как ты выяснил, что именно на value?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 16:56
ради интереса, поставь вместо append что-нибудь типа new char[] с размером того блока, который получаешь из query.
И вопрос по исключениям в тему.. Почему не ловится исключение?? Просто вылетает с сообщением о std::bad_alloc..

Код:
int main(int argc, char *argv[])
{
    while (1) {
        //

        try {
            //

            new char;

        } catch(std::bad_alloc) {
            //

            qDebug() << "BAD ALLOC";
            break;
        }
    }
    return 0;
}
Попробовал. Выделяет 1,4 ГБ
Код:
void tst_FtpWorker::initTestCase()
{
    int i = 250000000;
    int j = i;
    while (1)
    {
        try
        {
            new char[j];
            qDebug() << j;
            j = j + i;

        }
        catch(std::bad_alloc)
        {
            qDebug() << "BAD ALLOC";
            break;
        }
    }
}


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 16:59
не не не, я имел ввиду другое, ну да ладно, уже поздно  ;D


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 17:05
а как ты выяснил, что именно на value?
А вот
Код:
void tst_FtpWorker::initTestCase()
{
    try
    {
        QSqlDatabase udb;
        udb = QSqlDatabase::addDatabase( "QODBC", "conn" );
        udb.setDatabaseName( "Driver={SQL Server Native Client 10.0};Server=(local);Database=ks;UID=sa;PWD=1;" );
        udb.open();

        QSqlQuery query( udb );
        query.setForwardOnly(true);
        query.prepare("EXEC dbo.get_initial_data '56FEB471-37A7-494E-AF87-8FC03174C74F'");
        query.exec();
        query.first();
        query.value(1);
    }
    catch(std::bad_alloc)
    {
        qDebug() << "BAD ALLOC";
    }
}
bad_alloc на value. Данных в менеджере 248 метров (выделяет 500 метров и падает). Шайтан какой-то


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 17:21
так ты говорил в основном потоке все ок  ???


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 17:27
так ты говорил в основном потоке все ок  ???
У меня шляпа была со setStackSize


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 17:29
так ты говорил в основном потоке все ок  ???
У меня шляпа была со setStackSize
Поток, не поток - одинаково. QSqlQuery аллоциорать мои данные отказывается


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 17:45
В одной только записи уже 248 метров лежит?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 17:49
В одной только записи уже 248 метров лежит?
ога


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 17:49
В одной только записи уже 248 метров лежит?
ога
там FOR xml


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 19:00
Короче поток тут не причем
Скорее всего дело в append, как и говорили.
Код:
try
    {
        char * d = new char[1700000000];

        for (int i= 0; i < 1700000000- 1; ++i)
        {
            d[i] = 1;
        }
        delete[] d;
    }
    catch(std::bad_alloc)
    {
        qDebug() << "bad_alloc";
    }
все круто
Код:
try
    {
        QString l;

        while (l.length() < 300000000)
        {
            l.append("111111");
        }
    }
    catch(std::bad_alloc)
    {
        qDebug() << "bad_alloc";
    }
песта ((( QSqlQuery::value дает тот же результат - падает с bad_alloc


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: gil9red от Май 19, 2014, 20:38
Короче поток тут не причем
Скорее всего дело в append, как и говорили.
Код:
try
    {
        char * d = new char[1700000000];

        for (int i= 0; i < 1700000000- 1; ++i)
        {
            d[i] = 1;
        }
        delete[] d;
    }
    catch(std::bad_alloc)
    {
        qDebug() << "bad_alloc";
    }
все круто
Код:
try
    {
        QString l;

        while (l.length() < 300000000)
        {
            l.append("111111");
        }
    }
    catch(std::bad_alloc)
    {
        qDebug() << "bad_alloc";
    }
песта ((( QSqlQuery::value дает тот же результат - падает с bad_alloc

Код:
        while (l.length() < 300000000)
        {
            l.append("111111");
        }
Можно примерно подсчитать во сколько нам обойдется такой цикл:
"111111" = 12 байт
300000000 * 12 = 3600000000 байт = 3515625 кбайт = 3433,2275390625 мбайт = 3,35276126861572265625 гбайт
Если памяти будет не хватать, новую нельзя выделять

У вас оперативной памяти то хватит? :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: m_ax от Май 19, 2014, 20:54
Цитировать
300000000 * 12
Во-первых не на 12, а на 2 (sizeof(QChar)) + ещё некоторый процент на резерв.

Во-вторых, скажите, в чём разница между списком и вектором?)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: gil9red от Май 19, 2014, 21:15
Цитировать
300000000 * 12
Во-первых не на 12, а на 2 (sizeof(QChar)) + ещё некоторый процент на резерв.
"111111" - 6 символов, а т.к. это QString, то на один символ отделяется 2 байта, потому и 12 написал
Как я писал выше, это все примерно

Во-вторых, скажите, в чём разница между списком и вектором?)
Вектор - тот же сишный массив. В векторе данные хранятся последовательно.
Вектор - контейнер с произвольным доступом, лист - с последовательным.
Различаются разной скоростью получения элемента по индексу, а также вставкой и удалением элементов


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: m_ax от Май 19, 2014, 21:23
Цитировать
"111111" - 6 символов, а т.к. это QString, то на один символ отделяется 2 байта, потому и 12 написал
Понимаю, понедельник - день тяжёлый)
Код
C++ (Qt)
l.length() == 300000000 -1
 
В строке l (300000000 - 1) элементов QChar

Цитировать
Вектор - тот же сишный массив. В векторе данные хранятся последовательно.
Вектор - контейнер с произвольным доступом, лист - с последовательным.
Различаются разной скоростью получения элемента по индексу, а также вставкой и удалением элементов
Иии?

В чём ещё различие?)
Т.е., что происходит, когда в вектор добавляется элемент и что происходит со списком?)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: gil9red от Май 19, 2014, 21:57
Цитировать
"111111" - 6 символов, а т.к. это QString, то на один символ отделяется 2 байта, потому и 12 написал
Понимаю, понедельник - день тяжёлый)
Код
C++ (Qt)
l.length() == 300000000 -1
 
В строке l (300000000 - 1) элементов QChar
Тьфу, не обратил внимание :)

Т.е., что происходит, когда в вектор добавляется элемент и что происходит со списком?)
У списка создается новая вершина, которая хранит добавленного значение, эта вершина указывает на вершины слева и справа (двусвязный список)
У вектора сложнее - есть добавляется элемент в конец, тогда происходит перевыделение размера матрицы и элемент устанавливает в конец, если добавлятся в любое другое место вектора, то также выделяется место под добавляемый элемент, вот только перед вставкой, нужно все элементы справа от вставляемой позиции, передвинуть вправо, у списка бы просто нужны было поменять указатели вершин

Для вас проще было открыть вики и посмотреть там отличия массивов и списков :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 22:00
Лучше распишите формулу расчета нового размера для вектора или QString при добавлении элементов, которые не вместятся в текущий  ;)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: gil9red от Май 19, 2014, 22:07
Лучше распишите формулу расчета нового размера для вектора или QString при добавлении элементов, которые не вместятся в текущий  ;)

Наслаждайтесь :) Как говорится - из первых рук :) Еще горячий ))
Код:
QString &QString::append(const QString &str)
1871 {
1872     if (str.d != Data::sharedNull()) {
1873         if (d == Data::sharedNull()) {
1874             operator=(str);
1875         } else {
1876             if (d->ref.isShared() || uint(d->size + str.d->size) + 1u > d->alloc)
1877                 reallocData(uint(d->size + str.d->size) + 1u, true);
1878             memcpy(d->data() + d->size, str.d->data(), str.d->size * sizeof(QChar));
1879             d->size += str.d->size;
1880             d->data()[d->size] = '\0';
1881         }
1882     }
1883     return *this;
1884 }


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: OKTA от Май 19, 2014, 22:32
А вот и нет  ;D
Главная ф-ия вот эта, на сколько я понимаю. Только мой мозг отказывается разбираться сейчас  ;D
Код:
int qAllocMore(int alloc, int extra)
{
    Q_ASSERT(alloc >= 0 && extra >= 0);
    Q_ASSERT(alloc < (1 << 30) - extra);
 
    unsigned nalloc = alloc + extra;
 
    // Round up to next power of 2
 
    // Assuming container is growing, always overshoot
    //--nalloc;
 
    nalloc |= nalloc >> 1;
    nalloc |= nalloc >> 2;
    nalloc |= nalloc >> 4;
    nalloc |= nalloc >> 8;
    nalloc |= nalloc >> 16;
    ++nalloc;
 
    Q_ASSERT(nalloc > unsigned(alloc + extra));
 
    return nalloc - extra;
}


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 19, 2014, 22:35
Короче поток тут не причем
Скорее всего дело в append, как и говорили.
Код:
try
    {
        char * d = new char[1700000000];

        for (int i= 0; i < 1700000000- 1; ++i)
        {
            d[i] = 1;
        }
        delete[] d;
    }
    catch(std::bad_alloc)
    {
        qDebug() << "bad_alloc";
    }
все круто
Код:
try
    {
        QString l;

        while (l.length() < 300000000)
        {
            l.append("111111");
        }
    }
    catch(std::bad_alloc)
    {
        qDebug() << "bad_alloc";
    }
песта ((( QSqlQuery::value дает тот же результат - падает с bad_alloc

Код:
        while (l.length() < 300000000)
        {
            l.append("111111");
        }
Можно примерно подсчитать во сколько нам обойдется такой цикл:
"111111" = 12 байт
300000000 * 12 = 3600000000 байт = 3515625 кбайт = 3433,2275390625 мбайт = 3,35276126861572265625 гбайт
Если памяти будет не хватать, новую нельзя выделять

У вас оперативной памяти то хватит? :)
хватит. дело не в этом. падает гораздо раньше. выделяет 500 метров и при выделении следующих падает


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Май 20, 2014, 08:33
Короче поток тут не причем
Скорее всего дело в append, как и говорили.
Код:
try
    {
        char * d = new char[1700000000];

        for (int i= 0; i < 1700000000- 1; ++i)
        {
            d[i] = 1;
        }
        delete[] d;
    }
    catch(std::bad_alloc)
    {
        qDebug() << "bad_alloc";
    }
все круто
Код:
try
    {
        QString l;

        while (l.length() < 300000000)
        {
            l.append("111111");
        }
    }
    catch(std::bad_alloc)
    {
        qDebug() << "bad_alloc";
    }
песта ((( QSqlQuery::value дает тот же результат - падает с bad_alloc

Код:
        while (l.length() < 300000000)
        {
            l.append("111111");
        }
Можно примерно подсчитать во сколько нам обойдется такой цикл:
"111111" = 12 байт
300000000 * 12 = 3600000000 байт = 3515625 кбайт = 3433,2275390625 мбайт = 3,35276126861572265625 гбайт
Если памяти будет не хватать, новую нельзя выделять

У вас оперативной памяти то хватит? :)
зачем умножать 12 на 300000000 я не понял. там же по размеру


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: gil9red от Май 20, 2014, 09:34
Цитировать
зачем умножать 12 на 300000000 я не понял. там же по размеру
Уже ответил, что ошибся :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: cdsmika от Август 23, 2014, 01:42
Кому интересно вот в чем дело:
http://www.rsdn.ru/forum/cpp/913085.flat


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 23, 2014, 09:33
Кому интересно вот в чем дело:
http://www.rsdn.ru/forum/cpp/913085.flat
В чем дело было описано на первой же страницы этой темы.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Igors от Август 23, 2014, 09:49
В чем дело было описано на первой же страницы этой темы.
Открыл, но  так и не понял где  :)

Код:
while (l.length() < 300000000)
{
    l.append("111111");
}
Блок памяти слишком велик. 600 метров "чистого" + какой-то пул QString, вполне возможно всего около 1 Gb. Не все ОС могут выделять блок такого размера, даже если памяти хватает физически. Неск лет назад боролся с этой проблемой, пришел к выводу что хватать большие блоки - себе дороже, переделал на чанки, все проблемы исчезли.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 23, 2014, 09:59
Открыл, но  так и не понял где  :)
В посте m_ax, где он пишет о фрагментации адресного пространства.

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


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Igors от Август 23, 2014, 11:02
Фрагментировать память процесса (а точнее адресное пространство) можно и начиная с маленьких блоков, просто с большими блоками эффект проявляется быстрее.
Память выделяется страницами (обычно по 4K) которые выгружаются/подгружаются. Не знаю как можно  убить этот механизм дефрагментацией, надо сильно постараться. Выделение сводится к ф-ции типа vm_alloc которая уже собственно выделяет страницы, и, насколько я знаю, резервирует для них непрерывное дисковое пр-во. Тут да, могут быть проблемы.

Ну а чисто практически все сводится к банальной рекомендации "не хватайте слишком большие блоки, храните по частям (не переломитесь)"


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 23, 2014, 11:49
Память выделяется страницами (обычно по 4K) которые выгружаются/подгружаются. Не знаю как можно  убить этот механизм дефрагментацией, надо сильно постараться. Выделение сводится к ф-ции типа vm_alloc которая уже собственно выделяет страницы, и, насколько я знаю, резервирует для них непрерывное дисковое пр-во. Тут да, могут быть проблемы.
Вы сейчас выдали все, что когда то слышали о менеджерах памяти, причем обо всех. :)
Со страницами работает менеджер памяти ОС, который к менеджеру памяти из libc (который и реализует malloc/free) не имеет никакого отношения. Это два совершенно разных механизма, не смотря на то что последний иногда пользуется первым.
Да и проблема не в самой памяти (физической может быть хоть 100500Gb), а в фрагментации адресного пространства. На 32 битах это делается легко даже не большими блоками, как в вышеописанном примере. Главное, что бы последующий выделяемый блок был больше предыдущего.

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


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Igors от Август 23, 2014, 13:59
Со страницами работает менеджер памяти ОС, который к менеджеру памяти из libc (который и реализует malloc/free) не имеет никакого отношения. Это два совершенно разных механизма, не смотря на то что последний иногда пользуется первым.
Растущая QString требует один/непрерывный блок памяти, поэтому дело упрется в выделение страниц, на подробности malloc можно не обращать внимания.

Пример из этой темы показывает обратное, что фрагментировать можно и маленькими блоками.
Не увидел такого

Вы сейчас выдали все, что когда то слышали о менеджерах памяти, причем обо всех. :)
Не слышал, а изучил на своем опыте. Почему надо обязательно фыркнуть в лицо собеседнику? Что этим достигается? Только то что что пропадает всякое желание разговаривать со спесивым дустовцем  :) 


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 23, 2014, 14:09
Растущая QString требует один/непрерывный блок памяти, поэтому дело упрется в выделение страниц, на подробности malloc можно не обращать внимания.
Это просто набор слов, ничего не означающих. Уж простите, но это говорит о том, что вы не очень понимаете о чем говорите. :)
Еще раз простите, а то вы опять решите, что я фыркаю. :)
Найдите компьютер где много памяти, например, гигов 8 или более. Соберите 32 битное приложение с указанным в теме куском кода и запустите его. Вы удивитесь, но оно завершиться с сообщением об нехватки памяти. Но мы то знаем, что на машине 8 или даже больше гигабайт физической памяти. Так что же произошло? Каких страниц где не хватило? А ведь есть еще своп. :)

Не увидел такого
Сожалею. На самом деле там все очень просто, и я легко могу рассказать об этих процессах на пальцах. Только вам это не надо, для "работы" вам достаточно "якобы правильных" мифов:
Ну а чисто практически все сводится к банальной рекомендации "не хватайте слишком большие блоки, храните по частям (не переломитесь)"

Не слышал, а изучил на своем опыте.
Скорее всего поэтому вы не понимаете, что же происходит на самом деле и в чем причина происходящего.
Поэтому, как древние люди наблюдающие за молнией, решили для непознанного придумать мифы.
Ну что же - пожалуйста. :)



Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 23, 2014, 17:22
Вот маленькая демонстрация:
Код
C++ (Qt)
#include <iostream>
#include <QVector>
 
using namespace std;
 
int main()
{
       {
               cerr << "Allocate 1Gb" << endl;
               QVector<char> tst( 1024 * 1024 * 1024 );
       }
       {
               cerr << "Allocate..." << endl;
               size_t it = 0;
 
               QVector<char> vec;
               QVector<char> chunk( 1024 * 1024 );
               for(;;)
               {
                       vec += chunk;
                       cerr << it++ << " Mb" << endl;
               }
       }
}
 

Из 32 битных ОС у меня есть венда в virtualbox, под ней я могу спокойно выделить буфер в 1Гб (первый вектор), а вот во втором случае с выделением в цикле кусками по 1 Мб, у меня выделяет в общей сложности 510 Мб.
Чего же перестало хватать? Вот же, доли секунды назад был гиг (на самом деле под два), а сейчас всего 510 Мб? Куда там страницы делись? :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Nidxogg от Август 23, 2014, 17:51
Ну так и как с этим бороться?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 23, 2014, 20:01
Ну так и как с этим бороться?
Тут зависит на каком уровне вы хотите с этим разобраться.
Если на уровне мифов и легенд, то достаточно:
Ну а чисто практически все сводится к банальной рекомендации "не хватайте слишком большие блоки, храните по частям (не переломитесь)"
Точнее, на 32 битных платформах нужно очень тщательно продумывать выделения больших непрерывных участков памяти. Например, под вендой вы захватываете буфер в 500 Мб, вот расширить его до 1 Гб в два этапа по 250 Мб (вначале до 750 Мб, а потом до 1 Гб) у вас не получиться, хотя памяти для небольших блоков будет в избытке.

Или переходом на 64 битную платформу. Там проблемы с адресном пространством не возникнут еще долго, в связи с огромных его объемом. Хотя мы помним высказывание одного идиота о том, что 640 Кб хватит всем. Поэтому, лучше с памятью и адресным пространством разобраться один раз.

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



Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Nidxogg от Август 23, 2014, 20:29
Цитировать
Если будут вопросы пишите, попробую простым языком объяснить, что же там происходит с памятью.
Было бы интересно прочитать.

P.S Если в программе постоянно выделяется/освобождается память блоками по 3-5 мб, данная проблема с фрагментацией тоже будет иметь место?
Непрерывное время работы программы порядка пары недель.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 24, 2014, 07:43
Было бы интересно прочитать.
Давайте рассмотрим проблему фрагментации адресного пространства на микропроцессоре, который может адресовать только 64 байта памяти.
Все сказанное будет справедливо и для любого другого процессора, зато рисовать карту памяти процесса будет проще. :)

Это я нарисовал пустую карту памяти процесса:
[................................................................]

Загружаем в нее код и данные процесса, а так же добавляем стек и мапим код ядра:
[CCCCCCCCCCCDDDD...................................SSSKKKKKKKKKKK]

Здесь:
  с 00 по 10 байт хранится код программы
  с 11 по 14 байт данные программы
  c 15 по 49 байт свободная область памяти
  с 50 по 53 байт стек процесса
  с 54 по 63 байт ядро
  
Область для расположения кучи располагается с 15 байта и заканчивается на 49.

Давайте выделим блок памяти в 5 байт:
[CCCCCCCCCCCDDDDAAAAA..............................SSSKKKKKKKKKKK]

Менеджер памяти нам вернет указатель на 15 байт и зарезервирует 5 байт в куче.

Теперь мы хотим добавить к нашему блоку еще 3 байта, делается это в несколько этапов:
Вначале мы захватывает новый блок памяти с необходимым размером (5 байт + 3 байта = 8 байт):
[CCCCCCCCCCCDDDDAAAAABBBBBBBB......................SSSKKKKKKKKKKK]

Затем мы копируем в новый блок данные из строго и освобождаем последний:
[CCCCCCCCCCCDDDD.....BBBBBBBB......................SSSKKKKKKKKKKK]

Указатель на данные у нас уже указывает на 20 байт памяти.

Повторяем процесс еще раз:
[CCCCCCCCCCCDDDD.............CCCCCCCCCCC...........SSSKKKKKKKKKKK]

Все. Больше расширить блок на очередные 3 байта мы не сможем, хотя свободной памяти еще много.
Небольшие блоки будут выделяться легко, а вот большой непрерывный участок вызовет нехватку памяти.

Было бы не плохо уметь двигать блоки, но в C/C++ это не возможно, т.к. указатель это реальный адрес в адресном пространстве процесса и если переместить блок, то указатели станут не валидными.

Освободившиеся блоки памяти не всегда могут объединится в один большой блок, например, между ними может быть выделен небольшой блок, который не будет освобожден.
[CCCCCCCCCCCDDDD.....X..Y....CCCCCCCCCCC...........SSSKKKKKKKKKKK]


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Igors от Август 24, 2014, 08:00
Да и проблема не в самой памяти (физической может быть хоть 100500Gb), а в фрагментации адресного пространства. На 32 битах это делается легко даже не большими блоками, как в вышеописанном примере. Главное, что бы последующий выделяемый блок был больше предыдущего.
Это легко проверить
Код
C++ (Qt)
for (int i = 0; i < 1024; ++i) {
void * temp = malloc(i * 1024 * 1024);
       std::cerr << i << " Mb" << std::endl;
free(temp);
}
 
std::cerr << "done";
 
Это успешно доковыляет до "done".

Вот маленькая демонстрация:
...
Из 32 битных ОС у меня есть венда в virtualbox, под ней я могу спокойно выделить буфер в 1Гб (первый вектор), а вот во втором случае с выделением в цикле кусками по 1 Мб, у меня выделяет в общей сложности 510 Мб.
Чего же перестало хватать? Вот же, доли секунды назад был гиг (на самом деле под два), а сейчас всего 510 Мб? Куда там страницы делись? :)
Никуда не делись, это проблемы контейнера. Он имел 512 метров, сейчас надо распределить 1024 и скопировать туда содержимое. Это можно сымитировать так.
Код
C++ (Qt)
 
QVector<char> tst2(1024 * 1024 * (512));
std::cerr << "Allocate 1Gb" << std::endl;
QVector<char> tst(1024 * 1024 * (1024));
 
И получаете столь же успешный вылет.

Это просто набор слов, ничего не означающих. Уж простите, но это говорит о том, что вы не очень понимаете о чем говорите. :)
Говорите о деле/теме - и само собой станет ясно кто понимает, а кто так, "статейки читал" :) А бесконечные выпады "вы не понимаете" никому не интересны, не сваливайтесь в базар


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 24, 2014, 08:06
Это легко проверить
А так вы ничего не проверяете.
Нужно выделить новый блок и только после этого удалять старый. Как это делается в разных строках и векторах.

Никуда не делись, это проблемы контейнера. Он имел 512 метров, сейчас надо распределить 1024 и скопировать туда содержимое. Это можно сымитировать так.
Это проблем не контейнера, а адресного пространства. Контейнер память выделяет, через менеджер кучи, а он почему то не может ее выделить, хотя памяти валом.

Говорите о деле/теме - и само собой станет ясно кто понимает, а кто так, "статейки читал" :) А бесконечные выпады "вы не понимаете" никому не интересны, не сваливайтесь в базар
Если вы пишете не связный поток слов, то о деле говорить сложно. :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Igors от Август 24, 2014, 08:11
Давайте рассмотрим проблему фрагментации адресного пространства на микропроцессоре, который может адресовать только 64 байта памяти.
Все сказанное будет справедливо и для любого другого процессора, зато рисовать карту памяти процесса будет проще. :)
...

[CCCCCCCCCCCDDDD.............CCCCCCCCCCC...........SSSKKKKKKKKKKK]

Все. Больше расширить блок на очередные 3 байта мы не сможем, хотя свободной памяти еще много.
Небольшие блоки будут выделяться легко, а вот большой непрерывный участок вызовет нехватку памяти.

Было бы не плохо уметь двигать блоки, но в C/C++ это не возможно, т.к. указатель это реальный адрес в адресном пространстве процесса и если переместить блок, то указатели станут не валидными.
Зато ОС умеет. Вместо "байт" подставьте "страница" - и никакой проблемы нет. Непрерывный блок памяти (с точки зрения С/С++) может сидеть в физической памяти напр так
A....A...A...
Более того какие-то страницы могут быть вообще сброшены на диск. "Любая логическая страница может отображаться в любую физическую"


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 24, 2014, 08:17
Кстати, для тех кто хочет разобраться можно посмотреть как двигается буфер с данными по адресному пространству процессу.
Код
C++ (Qt)
#include <iostream>
#include <iomanip>
#include <QVector>
 
using namespace std;
 
int main()
{
{
cerr << "Allocate 1Gb" << endl;
QVector<char> tst( 1024 * 1024 * 1024 );
}
{
cerr << "Allocate..." << endl;
size_t it = 0;
 
QVector<char> vec;
QVector<char> chunk( 1024 * 1024 );
for(;;)
{
vec += chunk;
cerr << dec << it++ << " Mb" << "   addr = " << hex << reinterpret_cast<const void*>( vec.constData() ) << "   size = " << dec << vec.capacity() << endl;
}
}
}
 


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 24, 2014, 08:23
Зато ОС умеет. Вместо "байт" подставьте "страница" - и никакой проблемы нет.
Проблемы будут ровно те-же. :)

Давайте смотреть:
Мы выделили блок A, аллокатор вернул нам указатель на его начало - 0x100;
Мы выделили блок B, аллокатор вернул нам указатель на его начало - 0x150;
[...AAAAAAAABBBBBBBB.............................................]

Теперь мы хотим расширить блок A. Сможет ли ОС добавить в конец этого блока страниц для его расширения? Конечно нет.
Ведь тогда сместиться и положение блока B в адресном пространстве, а значит указатель (адрес) на его начало станет не валидным.
Поэтому, мы вынуждены выделять новый блок C и копировать туда содержимое A.
[...AAAAAAAABBBBBBBBCCCCCCCCCCCCCCCCCCCC.........................]

Непрерывный блок памяти (с точки зрения С/С++) может сидеть в физической памяти напр так
A....A...A...
Ну и что? Мы говорим о адресном пространстве процесса, где и куда ОС подставила реальные физические страницы памяти для нас и процесса совершенно не важно. В этом смысл страниц и виртуальной памяти вообще.

Более того какие-то страницы могут быть вообще сброшены на диск. "Любая логическая страница может отображаться в любую физическую"
И закончили все "важной" банальной фразой, никак не соотносящейся с обсуждаемой проблемой. :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 24, 2014, 17:32
Переделал демонстрашку. QVector захватывает память с запасом, поэтому что бы никого не путать решил переделать все на new/delete.

Код
C++ (Qt)
#include <iostream>
#include <iomanip>
 
using namespace std;
 
int main()
{
{
cerr << "Allocate..." << endl;
size_t it = 1;
 
char *oldPtr = new char[ it * 1024 * 1024 ];
for(;;)
{
size_t sz = ++it * 1024 * 1024;
 
char *newPtr = new char[ sz ];
delete[] oldPtr;
oldPtr = newPtr;
 
cerr << dec << it << " Mb" << "   addr = " << hex << reinterpret_cast<const void*>( oldPtr ) << "   " << sz << endl;
}
}
}
 

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

Но фрагментация свое дело знает и более 500 с копейками мегабайт мы получить не сможем. Хотя свободной памяти у нас еще чуть меньше полутора гигабайтов.

А если добавить еще аллокаций блоков, которые не будут освобождаться, как в реальной жизни. Что бы не всегда была возможность объединять свободные блоки, то мы упремся в мегабайт 400.



Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Igors от Август 25, 2014, 06:59
Копаться в длинной теме никто не хочет, поэтому коротенько повторю о чем речь. Смысл Вашего утверждения в том что если заполнить память чередующимися блоками и "дырками" - она окажется забитой и malloc вернет NULL для блока с большим размером чем все предыдущие.

o xx ooo xxxx ooooo ... // o - блок, x - дырка

Воспроизведем эту ситуевину (аттач). Распределенные блоки добавляем в мапу и просматриваем ее: если возможно, удаляем один из блоков создавая дырку заведомо меньшего размера. Результаты (бородатый OSX 10.6)

Цитировать
[80], alloc 1713 Mb (blocks 35), free 1525 Mb
[81], alloc 1714 Mb (blocks 35), free 1605 Mb
TestMem(2533,0xa048e540) malloc: *** mmap(size=85983232) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
can't alloc 81 Mb
Суммарный размер 1714 + 1605 = намного больше 2Gb, выходит дырки используются. Не исключено что на др ОС результаты будут другими. Ну в почему только 1.7 Gb а не все 2? Не знаю, ответа в свое время не нашел. Полагаю что ОС не может выделить непрерывное пр-во на диске, возможно есть какой-то предел и.т.п. Поскольку изменить это нельзя, не особо упорствовал в поисках.

Во всяком случае это не "особенность памяти" - с ней все норм, а специфика конкретного ОС/виртуалки. А по-вашему выходит что мы все еще "в крутых 90-х"  :)


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 25, 2014, 07:11
Смысл Вашего утверждения в том что если заполнить память чередующимися блоками и "дырками" - она окажется забитой и malloc вернет NULL для блока с большим размером чем все предыдущие.
Перечитайте хотя бы последние посты, а еще лучше запустите последнюю демонстрашку и разберитесь, почему не получается выделить больше 500 с копейками мегабайт при свободных чуть менее 2 гигабайтах. Всю необходимую информацию она печатает, по которой видно когда объединяются неиспользуемые блоки, где в адресном пространстве располагается блок.
Поэтому если взять листик и карандаш и рисовать примерное расположение блока в памяти процесса, то можно увидеть, что все сведется к диаграммам, которые я рисовал чуть выше. :)

По поводу 2 Гб - это ограничение венды, она отдается 2 Гб из 4 пользовательскому процессу, а 2 оставляет для ядра. У linux соотношение другое - 3 пользовательскому коду, 1 ядру. Это все для 32 битных процессов, которые не умеют адресовать более 4 Гб.

Во всяком случае это не "особенность памяти" - с ней все норм, а специфика конкретного ОС/виртуалки.
Это особенности всех процессоров совместно с flat-mode - особенности непрерывного адресного пространства. Я показывал этот эффект на диаграммах для машины с памятью в 64 байта, он будет аналогичным и для любых других объемов. Только время полной фрагментации адресного пространства придется ждать дольше.

А по-вашему выходит что мы все еще "в крутых 90-х"  :)
Так с 90-х особо ничего не изменилось. Разработчики вцепились в flat-mode и мы потеряли возможность использовать сегменты, а так бы даже на 32 битных платформах можно было использовать кучу сегментов, размером до 4 Гб, большие буферы легко бы расширялись и никаких проблем с фрагментацией адресного пространства просто не было.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Nidxogg от Август 25, 2014, 17:21
Цитировать
Только время полной фрагментации адресного пространства придется ждать дольше.
Когда такой момент настанет, как все-таки выйти из положения?


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Igors от Август 25, 2014, 18:59
Перечитайте хотя бы последние посты, а еще лучше запустите последнюю демонстрашку и разберитесь, ..
Да я-то еще много лет назад разобрался :) А разговаривать с "поучающим" больше не хочу

Когда такой момент настанет, как все-таки выйти из положения?
Ну когда настанет - придется переделывать. Конкретно у меня выделялись большие блоки (сотни метров). При этом вероятность заклинивания довольно высока. Переделал, стал выделять блоками по 64К - как рукой сняло.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 25, 2014, 19:06
Да я-то еще много лет назад разобрался :)

Ага, я же и смотрю. :)

Не знаю, ответа в свое время не нашел. Полагаю что ОС не может выделить непрерывное пр-во на диске, возможно есть какой-то предел и.т.п. Поскольку изменить это нельзя, не особо упорствовал в поисках.

Поэтому придумали ошибочную мантру:
Ну а чисто практически все сводится к банальной рекомендации "не хватайте слишком большие блоки, храните по частям (не переломитесь)"
Которой и пользуетесь до сих пор. :)
Ну не хотите разбираться, как хотите.


Название: Re: QSqlQuery+QThread = bad_alloc
Отправлено: Old от Август 25, 2014, 19:27
Когда такой момент настанет, как все-таки выйти из положения?
Когда наступит этот момент, то выходить из положения будет поздно. Такие моменты не стоит допускать.
Проблема возникает если нужны большие непрерывные блоки памяти, которым еще хочется расширять (т.е. выделять больший блок и копировать туда данные из старого блока).
Если в вашей программе нужны такие блоки, то стоит заранее продумать как их выделять. Например, можно сразу выделить большой блок в памяти и постепенно его заполнять, вместо постоянных переаллокаций все больших блоков.
С небольшими блоками таких проблем не случается, если конечно нет утечек.

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