Когда у игры неполучается выделить память, она показывает сообщение об ошибке "Недостаточно памяти для обработки команды".
Произойти это может, если память доступная процессу игры кончилась.
Даже если у вас 64-битный процессор и десятки гигабайт оперативной памяти, игра 32-битная и не может использовать больше 4-ех гигабайт памяти.
А на практике, даже больше 2-ух гигабайт будет проблемно занять, из-за особености устройства используемых варкрафтом связных списков.
Даже если у вас 64-битный процессор и десятки гигабайт оперативной памяти, игра 32-битная и не может использовать больше 4-ех гигабайт памяти.
А на практике, даже больше 2-ух гигабайт будет проблемно занять, из-за особености устройства используемых варкрафтом связных списков.
Также выделение может провалиться даже когда еще много свободной памяти.
Дело в том, что даже если сумарное количество свободной памяти достаточно велико, свободного пространства с одним цельным блоком нужного размера может всё равно не найтись.
Дело в том, что даже если сумарное количество свободной памяти достаточно велико, свободного пространства с одним цельным блоком нужного размера может всё равно не найтись.
Регионы памяти
В конце отчета об ошибке, создаваемого игрой при краше от нехватки памяти, можно увидеть таблицу регионов памяти.
Пример таблицы
12 2784 e:\Drive1\temp\buildwar3x\War3\Source\UI/CCommandButton.cpp(546)
1 528 .\FrameDef.cpp(69)
12 768 e:\drive1\temp\buildwar3x\war3\source\game\CFogMaskTable.h(98)
1 56 .\CGameUI.cpp(517)
1 512 e:\Drive1\temp\buildwar3x\War3\Source\WorldEdit/WEUtilities.cpp(3422)
286 12308 .\W32\OsISndCache.cpp(991)
143 4004 .?AUNATIVETOKEN@@(-2)
...
Первая колонка содержит количество выделенных блоков памяти, а вторая их сумарный размер.
Дальше идет либо имя исходного файла и номер строки, либо закодированое имя типа и отрицательное число.
Дальше идет либо имя исходного файла и номер строки, либо закодированое имя типа и отрицательное число.
По сигнатуре региона можно попытаться догадаться о причине произошедшего.
Например, если имя объекта CUnitListNode, то можно заподозрить утечки памяти, связанные с неудаляемыми групами в скрипте карты.
Например, если имя объекта CUnitListNode, то можно заподозрить утечки памяти, связанные с неудаляемыми групами в скрипте карты.
Читаемая таблица регионов
Для более удобного просмотра таблицы, предлагаю воспользоваться моим скриптом на питоне, который сортирует элементы таблицы и конвертирует их в более читаемый вид.
Установите python последнией версии (3.x), но может быть и на старых заработает.
Установите python последнией версии (3.x), но может быть и на старых заработает.
wc3memsort.py
import sys
import re
from contextlib import nullcontext
from collections import namedtuple
def open_input(path):
return open(path, 'rt') if path != '-' else nullcontext(sys.stdin)
def open_output(path):
return open(path, 'wt') if path != '-' else nullcontext(sys.stdout)
input_path = sys.argv[1]
output_path = sys.argv[2]
with open_input(input_path) as f:
input_data = f.readlines()
pattern = re.compile(r' *([0-9]+) +([0-9]+) +(.*)\((-?[0-9]+)\)')
MemoryRegion = namedtuple('MemoryRegion', ['blocks_count', 'size', 'source_file', 'source_line'])
def parse_line(line):
m = pattern.match(line)
if m:
return MemoryRegion(
blocks_count = int(m.group(1)),
size = int(m.group(2)),
source_file = m.group(3),
source_line = int(m.group(4)),
)
def parse_lines(lines):
for i, line in enumerate(lines):
parsed_line = parse_line(line)
if parsed_line:
yield parsed_line
else:
raise Exception(f'Failed to convert line number {i+1}.')
memory_regions = list(parse_lines(input_data))
total_size = sum(r.size for r in memory_regions)
sorted_memory_regions = sorted(memory_regions, reverse=True, key=lambda r: r.size)
formatted_memory_regions = (format_memory_region(r) for r in sorted_memory_regions)
def format_size(size):
suffixes = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB']
i = 0
while size >= 1024:
size /= 1024
i += 1
if i >= len(suffixes):
i = len(suffixes) - 1
if i > 0:
return f'{size:.2f} {suffixes[i]}'
else:
return f'{size} {suffixes[i]}'
def format_memory_region(r):
pretty_size = format_size(r.size)
return f'{pretty_size}\t{r.blocks_count}\t{r.source_file}\t{r.source_line}'
with open_output(output_path) as f:
f.write(f'total size: {format_size(total_size)}.\n')
f.write('\n'.join(formatted_memory_regions))
f.write('\n')
Применение:
python wc3memsort.py <путь к файлу с исходной таблицей> <путь к файлу для записи результата>
Вместо пути может быть указан минус "-", в таком случае для чтения или записи будет использоваться стандартный ввод/вывод.
На вход скрипт принимает не весь отчет об ошибке, а лишь фрагмент с таблицей находящийся в конце файла между разделяющими полосами.
На выходе пишется сначала сумарное количество памяти, занимаемое всеми регионами памяти, а затем таблица регионов, в которой первая колонка содержит размер региона памяти, а вторая количество блоков памяти. Дальше, как и в оригинале, идет сигнатура региона.
Пример результата:
total size: 670.44 MiB.
145.41 MiB 1584 .?AVCAgentBaseAbs@@ -2
62.51 MiB 95047 .\cmemblock.cpp 372
59.05 MiB 40199 .?AVC3Vector@NTempest@@ -2
51.49 MiB 1468 .?AUCustomObjectField@@ -2
51.28 MiB 729588 .\CDataAllocator.cpp 152
...