Russian Qt Forum
Ноябрь 23, 2024, 12:08 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 ... 5 6 [7]   Вниз
  Печать  
Автор Тема: QSqlQuery+QThread = bad_alloc  (Прочитано 43526 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #90 : Август 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));
 
И получаете столь же успешный вылет.

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

Сообщений: 4350



Просмотр профиля
« Ответ #91 : Август 24, 2014, 08:06 »

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

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

Говорите о деле/теме - и само собой станет ясно кто понимает, а кто так, "статейки читал" Улыбающийся А бесконечные выпады "вы не понимаете" никому не интересны, не сваливайтесь в базар
Если вы пишете не связный поток слов, то о деле говорить сложно. Улыбающийся
« Последнее редактирование: Август 24, 2014, 08:30 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #92 : Август 24, 2014, 08:11 »

Давайте рассмотрим проблему фрагментации адресного пространства на микропроцессоре, который может адресовать только 64 байта памяти.
Все сказанное будет справедливо и для любого другого процессора, зато рисовать карту памяти процесса будет проще. Улыбающийся
...

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

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

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

Сообщений: 4350



Просмотр профиля
« Ответ #93 : Август 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;
}
}
}
 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #94 : Август 24, 2014, 08:23 »

Зато ОС умеет. Вместо "байт" подставьте "страница" - и никакой проблемы нет.
Проблемы будут ровно те-же. Улыбающийся

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

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

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

Более того какие-то страницы могут быть вообще сброшены на диск. "Любая логическая страница может отображаться в любую физическую"
И закончили все "важной" банальной фразой, никак не соотносящейся с обсуждаемой проблемой. Улыбающийся
« Последнее редактирование: Август 24, 2014, 08:58 от Old » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #95 : Август 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.

« Последнее редактирование: Август 24, 2014, 17:47 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #96 : Август 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-х"  Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #97 : Август 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 Гб, большие буферы легко бы расширялись и никаких проблем с фрагментацией адресного пространства просто не было.
« Последнее редактирование: Август 25, 2014, 08:41 от Old » Записан
Nidxogg
Гость
« Ответ #98 : Август 25, 2014, 17:21 »

Цитировать
Только время полной фрагментации адресного пространства придется ждать дольше.
Когда такой момент настанет, как все-таки выйти из положения?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #99 : Август 25, 2014, 18:59 »

Перечитайте хотя бы последние посты, а еще лучше запустите последнюю демонстрашку и разберитесь, ..
Да я-то еще много лет назад разобрался Улыбающийся А разговаривать с "поучающим" больше не хочу

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

Сообщений: 4350



Просмотр профиля
« Ответ #100 : Август 25, 2014, 19:06 »

Да я-то еще много лет назад разобрался Улыбающийся

Ага, я же и смотрю. Улыбающийся

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

Поэтому придумали ошибочную мантру:
Ну а чисто практически все сводится к банальной рекомендации "не хватайте слишком большие блоки, храните по частям (не переломитесь)"
Которой и пользуетесь до сих пор. Улыбающийся
Ну не хотите разбираться, как хотите.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #101 : Август 25, 2014, 19:27 »

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

Главное это разобраться с причинами, а зная что может произойти, вы всегда сможете подобрать наилучшее решение для конкретной задачи.
« Последнее редактирование: Август 25, 2014, 19:45 от Old » Записан
Страниц: 1 ... 5 6 [7]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.276 секунд. Запросов: 23.