Фрагментация "кучи" и диспетчер памяти окон

PostgreSQL 9.0 или позже:

Последние версии Пост-ГРЭС (с конца 2010 года) имеют string_agg(expression, delimiter) функция, которая сделает точно что вопрос, для которого задают, даже позволяя Вам определить строку разделителя:

SELECT company_id, string_agg(employee, ', ')
FROM mytable
GROUP BY company_id;

Пост-ГРЭС 9.0 также добавила способность определить ORDER BY пункт в любом составном выражении ; иначе порядок не определен. Таким образом, можно теперь записать:

SELECT company_id, string_agg(employee, ', ' ORDER BY employee)
FROM mytable
GROUP BY company_id;

Или действительно:

SELECT string_agg(actor_name, ', ' ORDER BY first_appearance)

PostgreSQL 8.4 или позже:

PostgreSQL 8.4 (в 2009) представил агрегатная функция array_agg(expression) , которая связывает значения в массив. Тогда array_to_string() может использоваться для предоставления желаемого результата:

SELECT company_id, array_to_string(array_agg(employee), ', ')
FROM mytable
GROUP BY company_id;

string_agg для пред9.0 версий:

В случае, если любой сталкивается с этим поиском контейнера совместимости для пред9.0 баз данных, возможно реализовать все в [1 117] кроме ORDER BY пункт.

Так с ниже определения это должно работать то же в 9.x DB Пост-ГРЭС:

SELECT string_agg(name, '; ') AS semi_colon_separated_names FROM things;

, Но это будет синтаксической ошибкой:

SELECT string_agg(name, '; ' ORDER BY name) AS semi_colon_separated_names FROM things;
--> ERROR: syntax error at or near "ORDER"

Протестированный на PostgreSQL 8.3.

CREATE FUNCTION string_agg_transfn(text, text, text)
    RETURNS text AS 
    $
        BEGIN
            IF $1 IS NULL THEN
                RETURN $2;
            ELSE
                RETURN $1 || $3 || $2;
            END IF;
        END;
    $
    LANGUAGE plpgsql IMMUTABLE
COST 1;

CREATE AGGREGATE string_agg(text, text) (
    SFUNC=string_agg_transfn,
    STYPE=text
);

Пользовательские изменения (все версии Пост-ГРЭС)

До 9,0, не было никакой встроенной агрегатной функции для конкатенации строк. Самая простая пользовательская реализация ( предложенный Vajda Gabo в этом сообщении списка рассылки , среди многих других) должна использовать встроенное textcat функция (который стоит за || оператор):

CREATE AGGREGATE textcat_all(
  basetype    = text,
  sfunc       = textcat,
  stype       = text,
  initcond    = ''
);

Вот CREATE AGGREGATE документация.

Это просто склеивает все строки без разделителя. Для получения", "вставленный промежуточный их, не имея его в конце, Вы могли бы хотеть сделать свою собственную функцию конкатенации и заменить им "textcat" выше. Вот тот, который я соединил и протестировал на 8.3.12:

CREATE FUNCTION commacat(acc text, instr text) RETURNS text AS $
  BEGIN
    IF acc IS NULL OR acc = '' THEN
      RETURN instr;
    ELSE
      RETURN acc || ', ' || instr;
    END IF;
  END;
$ LANGUAGE plpgsql;

Эта версия произведет запятую, даже если значение в строке будет пустым или пустым, таким образом, Вы производитесь как это:

a, b, c, , e, , g

, Если Вы предпочли бы удалять дополнительные запятые для вывода этого:

a, b, c, e, g

Тогда добавляют ELSIF проверка к функции как это:

CREATE FUNCTION commacat_ignore_nulls(acc text, instr text) RETURNS text AS $
  BEGIN
    IF acc IS NULL OR acc = '' THEN
      RETURN instr;
    ELSIF instr IS NULL OR instr = '' THEN
      RETURN acc;
    ELSE
      RETURN acc || ', ' || instr;
    END IF;
  END;
$ LANGUAGE plpgsql;
9
задан Community 23 May 2017 в 12:09
поделиться

5 ответов

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

Обычно тонкие срезы в фиксированных точках адресного пространства вызываются загрузкой связанных DLL по их предпочтительному адресу. Те, которые загружаются высоко в адресном пространстве, обычно являются dll операционной системы Microsoft. Для операционной системы более эффективно, если все они могут быть загружены по их предпочтительным адресам, потому что тогда части DLL, доступные только для чтения, могут использоваться всеми процессами.

Срез, который вы видите, не о чем беспокоиться, это почти ничего не вырезает из вашего адресного пространства. Как вы заметили, существуют библиотеки DLL, которые загружаются в других точках адресного пространства. IIRC shlwapi.dll - особенно плохой пример, загрузка примерно с 0x2000000 (снова IIRC), которая часто разбивает большую часть доступного адресного пространства на две меньшие части. Проблема заключается в том, что после загрузки DLL вы ничего не можете сделать, чтобы переместить это выделенное пространство.

Если вы связываете DLL (напрямую или через другую DLL), вы ничего не можете сделать. делать. Если вы используете LoadLibrary , вы можете скрытно зарезервировать его предпочтительный адрес, заставив его перемещаться - часто куда-нибудь лучше в адресном пространстве - перед освобождением этой зарезервированной памяти. Однако это не всегда работает.

Под капотом Address Space Monitor использует VirtualQueryEx для проверки адресного пространства процесса, но есть еще один вызов из библиотеки psapi, который используют другие инструменты (например, Process Explorer ), который может показать вам, какие файлы (включая библиотеки DLL) отображены в какие части адресного пространства.

Как вы обнаружили, может быть пугающе легко выбежать из места в 2 ГБ адресного пространства пользователя. По сути, лучшая защита от фрагментации памяти - это просто не требовать больших непрерывных блоков памяти. Несмотря на то, что его сложно модернизировать, проектирование вашего приложения для работы с фрагментами «среднего размера» обычно позволяет значительно более эффективно использовать адресное пространство.

Аналогичным образом вы можете использовать стратегию подкачки, возможно, используя файлы с отображением памяти или Address Расширения окон .

может быть пугающе легко исчерпать место в адресном пространстве пользователя 2 ГБ. По сути, лучшая защита от фрагментации памяти - это просто не требовать больших непрерывных блоков памяти. Несмотря на то, что его сложно модернизировать, проектирование вашего приложения для работы с фрагментами «среднего размера» обычно позволяет значительно более эффективно использовать адресное пространство.

Аналогичным образом вы можете использовать стратегию подкачки, возможно, используя файлы с отображением памяти или Address Расширения окон .

может быть пугающе легко исчерпать место в адресном пространстве пользователя 2 ГБ. По сути, лучшая защита от фрагментации памяти - это просто не требовать больших непрерывных блоков памяти. Несмотря на то, что его сложно модернизировать, проектирование вашего приложения для работы с фрагментами «среднего размера» обычно позволяет значительно более эффективно использовать адресное пространство.

Аналогичным образом вы можете использовать стратегию подкачки, возможно, используя файлы с отображением памяти или Address Расширения окон .

12
ответ дан 4 December 2019 в 11:42
поделиться

Я предполагаю, что вы часто выделяете и освобождение объектов разного размера и что именно это приводит к проблемам фрагментации памяти?

Существуют различные стратегии, позволяющие обойти эти проблемы; Различные менеджеры памяти, о которых вы упомянули, могут помочь, если они могут решить проблему фрагментации за вас, но для этого потребуется немного больше анализа основных причин фрагментации. Например, Если вы часто выделяете объекты трех или четырех типов, и это имеет тенденцию усугублять проблему фрагментации памяти, вы можете поместить их в собственные пулы памяти, чтобы обеспечить повторное использование блоков памяти правильного размера. Таким образом, у вас должен быть набор доступных блоков памяти, которые подходят для этого конкретного объекта и предотвращают распространенный сценарий, когда выделение объекта X разделяет блок памяти, достаточно большой, чтобы удерживать Y, таким образом, что вы внезапно не можете выделить какие-либо Y больше.

Что касается (2), я не знаю обертки вокруг nedmalloc (честно говоря, я не очень знаком с nedmalloc), но вы можете очень легко создавать свои собственные оболочки, так как вы можете либо создавать специфичные для класса операторы new и delete или даже перегружают / заменяют глобальные операторы new и delete. Я не большой поклонник последнего, но если ваше распределение "

2
ответ дан 4 December 2019 в 11:42
поделиться

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

2
ответ дан 4 December 2019 в 11:42
поделиться

Может быть, это исполняемый файл? Его нужно где-то загрузить в адресное пространство ....

Что касается 2, довольно легко переопределить глобальные функции new и delete ... просто определите их.

1
ответ дан 4 December 2019 в 11:42
поделиться

Лучший способ узнать, где выделена память в вашей программе, - это использовать отладчик. Существуют распределения для каждой загруженной DLL и самого исполняемого файла, и все они фрагментируют виртуальную память. Кроме того, использование библиотек C / C ++ и Windows API приведет к созданию кучи в вашем приложении, которая, по крайней мере, зарезервирует часть виртуальной памяти.

Вы можете, например, использовать VirtualAlloc, чтобы зарезервировать большой кусок виртуальной памяти. в относительно небольшой программе только для того, чтобы обнаружить, что либо VirtualAlloc не работает, либо приложение не работает позже, когда оно пытается загрузить новую DLL (и т. д.). Вы также не всегда можете контролировать, какие библиотеки DLL будут загружены и где. Многие аудио / видео и другие продукты внедряют библиотеки DLL во все запущенные процессы при запуске. Когда это произойдет, эти библиотеки DLL часто сначала выбирают адреса загрузки, то есть их скомпилированные / связанные по умолчанию, скорее всего, будут предоставлены. Из доступных 2 ГБ виртуального адресного пространства типичного 32-разрядного приложения Windows, если DLL загружается прямо в середине этого адресного пространства, наибольшее единичное выделение / резервирование, которое вы можете получить, будет меньше 1 ГБ.

Если вы используйте windbg, вы можете увидеть, какие области памяти используются, зарезервированы и т. д. Команда lm покажет вам адреса загрузки всех библиотек DLL и EXE, а также их диапазон. Команда! Vadump покажет вам всю виртуальную память, используемую процессом, и средства защиты страницы. Защита страницы - большой намек на то, что там есть. Например, в следующем (частичном)! Vadump из 64-битного процесса calc.exe вы увидите, что первая область - это просто диапазон виртуальной памяти, защищенной от доступа. (Среди прочего, это не позволяет вам выделять память по адресу 0.) MEM_COMMIT означает, что память поддерживается ОЗУ или файлом подкачки. PAGE_READWRITE - это, возможно, куча памяти или сегмент данных загруженного модуля. PAGE_READEXECUTE обычно загружается и отображается в списке, созданном lm. MEM_RESERVE означает, что что-то вызвало VirtualAlloc, чтобы зарезервировать область памяти, но она не отображается диспетчером виртуальной памяти и так далее ...

0:004> !vadump
BaseAddress:       0000000000000000
RegionSize:        0000000000010000
State:             00010000  MEM_FREE
Protect:           00000001  PAGE_NOACCESS

BaseAddress:       0000000000010000
RegionSize:        0000000000010000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00040000  MEM_MAPPED

BaseAddress:       0000000000020000
RegionSize:        0000000000003000
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              00040000  MEM_MAPPED

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

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

или сегмент данных загруженного модуля. PAGE_READEXECUTE - это обычно код, который загружается и отображается в списке, созданном lm. MEM_RESERVE означает, что что-то вызвало VirtualAlloc, чтобы зарезервировать область памяти, но она не отображается диспетчером виртуальной памяти и так далее ...

0:004> !vadump
BaseAddress:       0000000000000000
RegionSize:        0000000000010000
State:             00010000  MEM_FREE
Protect:           00000001  PAGE_NOACCESS

BaseAddress:       0000000000010000
RegionSize:        0000000000010000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00040000  MEM_MAPPED

BaseAddress:       0000000000020000
RegionSize:        0000000000003000
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              00040000  MEM_MAPPED

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

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

или сегмент данных загруженного модуля. PAGE_READEXECUTE - это обычно код, который загружается и отображается в списке, созданном lm. MEM_RESERVE означает, что что-то вызвало VirtualAlloc, чтобы зарезервировать область памяти, но она не отображается диспетчером виртуальной памяти и так далее ...

0:004> !vadump
BaseAddress:       0000000000000000
RegionSize:        0000000000010000
State:             00010000  MEM_FREE
Protect:           00000001  PAGE_NOACCESS

BaseAddress:       0000000000010000
RegionSize:        0000000000010000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00040000  MEM_MAPPED

BaseAddress:       0000000000020000
RegionSize:        0000000000003000
State:             00001000  MEM_COMMIT
Protect:           00000002  PAGE_READONLY
Type:              00040000  MEM_MAPPED

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

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

1
ответ дан 4 December 2019 в 11:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: