Защо първото C++ (m)разпределение винаги е 72 KB?
Коментари
Mewayz Team
Editorial Team
Мистерията зад вашето първо разпределение на C++
Вие пишете проста C++ програма. Единнов вътр. Четири байта. Пускате strace или любимия си профилиращ памет и ето го — процесът ви току-що поиска приблизително 72 KB от операционната система. Не 4 байта. Не 64 байта. Цели 72 KB. Ако някога сте се взряли в това число и сте се чудили дали вашите инструменти ви лъжат, не сте сами. Това на пръв поглед странно поведение е един от най-често задаваните въпроси сред разработчиците на C++, които за първи път се задълбочават във вътрешността на паметта, и отговорът ни отвежда на увлекателно пътешествие през слоевете, които стоят между вашия код и действителния хардуер.
Какво се случва, когато се обадите на new
За да разберете цифрата от 72 KB, трябва да проследите цялата верига на разпределение. Когато вашият C++ код изпълни new int, компилаторът превежда това в извикване на operator new, което на повечето Linux системи делегира на malloc от glibc. Но malloc не пита директно ядрото за 4 байта памет. Ядрото работи на страници - обикновено 4 KB на x86_64 - и цената на системно повикване е огромна в сравнение с обикновен достъп до паметта. Извикването на brk() или mmap() за всяко отделно разпределение би накарало всяка нетривиална програма да спре.
Вместо това разпределителят на памет на glibc — имплементация, наречена ptmalloc2, която сама по себе си произлиза от класическия dlmalloc на Дъг Леа — действа като посредник. Той изисква големи блокове памет от ядрото предварително, след което ги разделя на по-малки части, когато вашата програма се нуждае от тях. Това е основната причина първото ви разпределение на 4 байта да задейства много по-голяма заявка към операционната система. Разпределителят не е разточителен. Стратегически е.
Дисекция на 72 KB: Къде отиват байтовете
Първоначалното разпределение идва от няколко отделни компонента, които времето за изпълнение трябва да инициализира, преди да може да ви даде дори един байт използваема памет. Разбирането на всеки компонент обяснява защо числото попада там, където попада.
Първо, malloc на glibc инициализира главната арена — основната счетоводна структура, която проследява всички разпределения в главната нишка. Тази арена включва метаданни за купчината, указатели за свободен списък и структури на контейнери за различни размери на разпределение. Разпределителят разширява прекъсването на програмата чрез sbrk(), а първоначалното разширение се управлява от вътрешен параметър, наречен M_TOP_PAD, който по подразбиране е 128 KB подплата. Действителната първоначална заявка обаче се коригира за подравняване на страницата и съществуваща позиция на прекъсване, което често води до по-малка първа заявка - обикновено достигаща до тази цифра от 72 KB при току-що стартиран процес.
Второ, от glibc 2.26 насам разпределителят инициализира локален кеш за нишки (tcache) при първа употреба. Tcache съдържа 64 контейнера (по един за клас с малък размер на разпределението), като всеки може да побере до 7 кеширани парчета. Самият tcache_perthread_struct консумира около 1 KB, но актът на инициализиране задейства по-широката настройка на арената. Трето, средата за изпълнение на C++ вече е извършила разпределения, преди вашият main() дори да се изпълни – статични конструктори, инициализация на буфера на iostream за std::cout и приятели, както и настройката на локала, всички те допринасят за този първоначален отпечатък на купчина.
Системата Arena и защо предварителното разпределение е умно
Решението за предварително разпределяне на значителна част от паметта, вместо да се иска на части, не е случайно изпълнение. Това е умишлен инженерен компромис, вкоренен в десетилетия опит в системното програмиране. Всяко извикване на brk() или mmap() включва контекстно превключване от потребителско пространство към пространство на ядрото, модификация на съпоставянията на виртуалната памет на процеса и потенциални актуализации на таблицата на страниците. На модерен хардуер едно системно повикване струва приблизително 100-200 наносекунди – тривиално в изолация, катастрофално в мащаб.
Вземете предвид програма, която прави 10 000 малки разпределения по време на инициализация. Без предварително разпределение това би означавало 10 000 системни повиквания, струващи приблизително 1-2 милисекунди чисти режийни разходи. С разпределител, базиран на арена, първото разпределение задейства едно системно извикване, а следващите 9999 разпределения се обслужват изцяло в потребителското пространство чрез аритметика на указателя и свързани списъчни операции — всяка от които отнема приблизително 10-50 наносекунди. Математиката е недвусмислена: предварителното разпределение печели на порядък.
<блоков цитат>72 KB, които виждате при първото си разпределение, не са загубена памет — това е инвестиция в ефективност. Разпределителят залага, че вашата програма скоро ще направи повече разпределения и в почти всеки сценарий от реалния свят този залог се изплаща щедро. Цената на неизползваното виртуално адресно пространство е по същество нула в съвременните 64-битови системи.
Виртуална памет срещу физическа памет: защо няма значение
Обща грижа сред разработчиците, които се сблъскват с това поведение за първи път, е загубата на ресурси. Ако имам нужда само от 4 байта, защо програмата ми консумира 72 KB? Критичното прозрение е, че виртуалната памет не е физическа памет. Когато glibc удължи прекъсването на програмата с 72 KB, ядрото актуализира съпоставянията на виртуалната памет на процеса, но не поддържа веднага тези страници с физическа RAM. Действителните физически страници се разпределят при поискване чрез страница грешки — само когато вашата програма пише на определен адрес, ядрото присвоява реална страница от паметта към нея.
💡 DID YOU KNOW?
Mewayz replaces 8+ business tools in one platform
CRM · Invoicing · HR · Projects · Booking · eCommerce · POS · Analytics. Free forever plan available.
Start Free →Това означава, че въпреки че виртуалният размер на вашия процес се увеличава със 72 KB, неговият размер на резидентния набор (RSS) – действително консумираното количество физическа RAM – се увеличава само със страниците, които действително докосвате. За едно ново int това обикновено е една страница от 4 KB плюс всички страници, които заемат метаданните на арената. Оставащото виртуално пространство стои там, готово за използване, не струва нищо освен адресно пространство – от което имате 128 TB на 64-битова Linux система.
Това разграничение е критично при профилиране и наблюдение на производствени приложения. Ако изграждате софтуер, който трябва да проследява реалното потребление на ресурси — независимо дали става дума за бекенд на SaaS, микроуслуга или тръбопровод за анализ като тези, които работят на платформи като Mewayz за бизнес операции — винаги трябва да наблюдавате RSS, а не виртуалния размер. Инструменти като /proc/[pid]/smaps, valgrind --tool=massif и pmap могат да ви дадат точни отпечатъци от физическата памет, вместо подвеждащи данни за виртуалната памет.
Как различните разпределители обработват първото разпределение
Цифрата от 72 KB е специфична за ptmalloc2 на glibc. Други разпределители правят различни компромиси и първоначалните разходи за разпределение варират съответно. Разбирането на тези разлики е ценно при избора на разпределител за чувствителни към производителността приложения.
- jemalloc (използван от Facebook, FreeBSD) — Използва по-подробна арена структура с локални кешове за нишки. Първоначалните режийни разходи обикновено са по-високи (често 200+ KB), но осигуряват по-добра многонишкова производителност поради намалено съревнование за заключване.
- tcmalloc (Thread-Caching Malloc на Google) — Разпределя кеш за нишка от приблизително 2 MB по подразбиране, с агресивно предварително разпределение. Първоначалните разходи са по-високи, но следващите малки разпределения са изключително бързи.
- malloc на musl libc — Използва много по-прост дизайн, базиран на mmap за всички разпределения. Първоначалните разходи са минимални (често само 4 KB на разпределение), но цената на разпределение е по-висока поради по-честите системни извиквания.
- mimalloc (Microsoft) — Използва разпределение на базата на сегменти с 64 MB сегменти. Първото разпределение задейства 64 MB виртуална резервация (с минимален физически ангажимент), търгуващо адресно пространство за изключителна локалност и пропускателна способност.
Изборът между тези разпределители зависи изцяло от вашето работно натоварване. За дълго работещи сървърни приложения с тежко многопоточно разпределение, jemalloc или tcmalloc обикновено превъзхождат glibc по подразбиране. За вградени системи с ограничена памет по-простият подход на musl може да бъде за предпочитане въпреки по-ниската производителност. За повечето настолни и сървърни приложения с общо предназначение първоначалните 72 KB разходи на ptmalloc2 представляват разумна стойност по подразбиране, която работи добре без настройка.
Настройка на поведението на първоначалното разпределение
Ако първоначалните разходи по подразбиране от 72 KB са наистина проблематични за вашия случай на употреба — може би раждате хиляди краткотрайни процеси, всеки от които прави само няколко разпределения — glibc предоставя няколко настройки чрез mallopt() и MALLOC_ семейството от променливи на средата.
Параметърът M_TOP_PAD контролира колко допълнителна памет изисква разпределителят извън това, което е необходимо веднага. Задаването му на 0 с mallopt(M_TOP_PAD, 0) казва на разпределителя да изисква само това, което е необходимо, намалявайки значително първоначалните разходи. Параметърът M_MMAP_THRESHOLD контролира размера, над който разпределенията използват mmap вместо арената. M_TRIM_THRESHOLD контролира кога освободената памет се връща към операционната система. И от glibc 2.26, регулируемите glibc.malloc.tcache_count и glibc.malloc.tcache_max ви позволяват да контролирате поведението на кеша на нишките.
Въпреки това, едно предупреждение: настройването на тези параметри без внимателен сравнителен анализ почти винаги влошава нещата. Стойностите по подразбиране са избрани въз основа на обширно профилиране в реалния свят и представляват подходящо място за по-голямата част от работните натоварвания. Освен ако нямате сериозни доказателства от профилирането на продукцията, че malloc overhead е тясно място — и сте измерили въздействието на вашите промени — оставете настройките по подразбиране. Преждевременното оптимизиране на разпределителя е особено коварна форма на бръснене на якове, която е погълнала безброй инженерни часове за незначителна полза.
Какво ни учи това за системното програмиране
Мистерията за първо разпределение на 72 KB е в основата си урок за слоевете на абстракция. C++ ви създава илюзията, че new int разпределя 4 байта. Езиковият стандарт го казва. Вашият умствен модел казва така. Но между вашия код и хардуера стои набор от усъвършенствани системи — среда за изпълнение на C++, разпределител на библиотека C, подсистема за виртуална памет на ядрото и MMU и TLB на хардуера — всяка добавяща собствено поведение, оптимизации и допълнителни разходи.
Това не е недостатък. Това е целият смисъл на системния софтуер. Всеки слой съществува, за да реши реален проблем: разпределителят съществува, така че не е нужно да правите системни извиквания за всяко разпределение. Системата за виртуална памет съществува, така че не е необходимо да управлявате директно физическата памет. Манипулаторът за грешка на страницата съществува, така че паметта се ангажира лениво и ефективно. Всеки слой разменя малко количество прозрачност за голямо количество производителност и удобство.
Разработчиците, които изграждат най-надеждните системи с най-висока производителност, са тези, които разбират тези слоеве — не защото трябва постоянно да мислят за тях, а защото когато се случи нещо неочаквано (като мистериозно разпределение на 72 KB), те имат менталния модел, за да разберат защо. Независимо дали изграждате система за търговия в реално време, двигател за игри или бизнес платформа, обслужваща хиляди потребители, способността да разсъждавате какво всъщност прави вашият код на системно ниво е това, което отличава компетентните разработчици от изключителните. 72 KB не са грешка. Това е вашият разпределител, който върши работата си брилянтно.
Изградете своята бизнес операционна система днес
От фрийлансъри до агенции, Mewayz захранва 138 000+ бизнеса с 207 интегрирани модула. Започнете безплатно, надстройте, когато пораснете.
Създайте безплатен акаунт →Try Mewayz Free
All-in-one platform for CRM, invoicing, projects, HR & more. No credit card required.
Get more articles like this
Weekly business tips and product updates. Free forever.
You're subscribed!
Start managing your business smarter today
Join 30,000+ businesses. Free forever plan · No credit card required.
Ready to put this into practice?
Join 30,000+ businesses using Mewayz. Free forever plan — no credit card required.
Start Free Trial →Related articles
Hacker News
Show HN: Spice simulation → oscilloscope → verification with Claude Code
Apr 17, 2026
Hacker News
Hospital at centre of child HIV outbreak caught reusing syringes in Pakistan
Apr 16, 2026
Hacker News
George Orwell Predicted the Rise of "AI Slop" in Nineteen Eighty-Four (1949)
Apr 16, 2026
Hacker News
Everything we like is a psyop
Apr 16, 2026
Hacker News
U.S. to Create High-Tech Manufacturing Zone in Philippines
Apr 16, 2026
Hacker News
New unsealed records reveal Amazon's price-fixing tactics, California AG claims
Apr 16, 2026
Ready to take action?
Start your free Mewayz trial today
All-in-one business platform. No credit card required.
Start Free →14-day free trial · No credit card · Cancel anytime