Кіріспе
C бағдарламалау тіліндегі динамикалық жадты басқару – C бағдарламалау тілінде динамикалық жадты бөлуді C стандартты кітапханасындағы функциялар тобы арқылы қолмен басқаруды білдіреді, атап айтқанда , , , және . C++ бағдарламалау тілі де осы функцияларды қамтиды; алайда, операторлар және осыған ұқсас мүмкіндіктерді ұсынады және осы тіл авторлары оларды қолдануды ұсынады. Дегенмен, жаңа/өшіру операторларын қолдануға болмайтын жағдайлар бар, мысалы, қоқыс жинау коды немесе өнімділікке сезімтал код, және жоғары деңгейдегі жаңа оператордың орнына malloc және placement new комбинациясы қажет болуы мүмкін. жадты бөлу механизмінің көптеген әртүрлі іске асырылымдары бар. Олардың орындалу уақыты және қажетті жад көлемі әртүрлі болады.
C dynamic memory allocation refers to performing manual memory management for dynamic memory allocation in the C programming language via a group of functions in the C standard library, namely , , , and
The C++ programming language includes these functions; however, the operators and provide similar functionality and are recommended by that language's authors. Still, there are several situations in which using new/delete is not applicable, such as garbage collection code or performance sensitive code, and a combination of malloc and placement new may be required instead of the higher level new operator. Many different implementations of the actual memory allocation mechanism, used by , are available. Their performance varies in both execution time and required memory.
Негізгі себептері
C бағдарламалау тілі жадыны статикалық, автоматты немесе динамикалық түрде басқарады. Статикалық ұзақтықтағы айнымалылар негізгі жадта, әдетте бағдарламаның орындалатын кодымен бірге бөлінеді және бағдарламаның өмір бойы сақталады; автоматты ұзақтықтағы айнымалылар стекте бөлінеді және функциялар шақырылғанда және қайтарылғанда пайда болып, жоғалады. Статикалық және автоматты ұзақтықтағы айнымалылар үшін бөлінген жадтың мөлшері компиляция уақытында тұрақты болуы керек (өзгермелі ұзындықтағы автоматты массивлерден басқа). Егер қажетті мөлшері орындалу уақытына дейін белгісіз болса (мысалы, пайдаланушыдан немесе дискідегі файлдан кез келген мөлшердегі деректер оқылса), онда алдын ала белгіленген мөлшердегі деректер объектілерін пайдалану жеткіліксіз болады. Бөлінген жадтың өмір сүру мерзімі де маңызды мәселе. Статикалық немесе автоматты жадтың ұзақтығы барлық жағдайларға сай келмейді. Автоматты түрде бөлінген деректер бірнеше функция шақыруларында сақталмайды, ал статикалық деректер бағдарламаның өмір бойына қажет болсын, болмасын сақталады. Көп жағдайда бағдарламашы бөлінген жадтың өмір сүру мерзімін басқаруда көбірек икемділікке мұқтаж болады. Бұл шектеулер динамикалық жадты бөлу арқылы еңсеріледі, онда жад нақтырақ (бірақ икемдірек) басқарылады, әдетте оны осы мақсат үшін құрылған жадтың бөлігінен бөлу арқылы. C тілінде malloc кітапханалық функциясы жад блогын үйіндіге бөлу үшін қолданылады. Бағдарлама осы жад блогына malloc қайтаратын мекенжай арқылы кіреді. Жад енді қажет болмағанда, мекенжай free функциясына жіберіледі, ол жадты босатады, осылайша оны басқа мақсаттар үшін пайдалануға болады. C тілінің бастапқы сипаттамасында calloc және cfree стандартты кітапханада болғаны, ал malloc болмағаны көрсетілген. Unix үшін жадты басқарудың қарапайым үлгісі alloc және free функцияларымен пайдаланушы интерфейсі ретінде берілген, ал sbrk жүйелік шақыруы операциялық жүйеден жад сұрау үшін қолданылған. Unix-тің 6-шы нұсқаулығында alloc және free функциялары төменгі деңгейлі жадты бөлу функциялары ретінде келтірілген. Қазіргі заманғы malloc және free процедуралары толыққанды түрде Unix-тің 7-ші нұсқаулығында сипатталған. Кейбір платформалар C стегінен емес, үйіндіден (мысалы, alloca) орындалу уақытында динамикалық жадты бөлуге мүмкіндік беретін кітапханалық немесе кіріктірілген функцияларды ұсынады. Бұл жад шақыру функциясы аяқталғанда автоматты түрде босатылады.
Функциялардың жалпы сипаттамасы
C динамикалық жадты бөлу функциялары stdlib.h зақыпысында (C++-дағы cstdlib зақыпысында) анықталған.
Құюдың артықшылықтары
Құрастыруды қосу C бағдарламасын немесе функциясын C++ ретінде компиляциялауға мүмкіндік береді. Бұл құрастыру 1989 жылға дейінгі `malloc` нұсқаларымен үйлесімділікті қамтамасыз етеді, олар бастапқыда `char *` типті қайтаратын. Құрастыру әзірлеушіге мақсатты көрсеткіштің типі өзгерген кезде тип өлшемдеріндегі қателерді анықтауға көмектеседі, әсіресе егер көрсеткіш `malloc` шақыруынан алыс жерде жарияланған болса (дегенмен, қазіргі заманғы компиляторлар мен статикалық талдаушылар құрастыруды қажет етмей-ақ мұндай жағдайлар туралы ескерте алады).
Құюдың кемшіліктері
C стандарты бойынша, түрлендіру артық. Түрлендіруді қосу stdlib.h деген қаперді қоспау қатесін жасыруы мүмкін, онда malloc функциясының прототипі табылады. Malloc прототипі болмаған жағдайда, C90 стандарты C компиляторы malloc int типті мән қайырады деп есептейді. Егер түрлендіру болмаса, C90 осы бүтін сан көрсеткішке тағайындалғанда диагностикалық хабарлама беруді талап етеді; бірақ түрлендіру болғанда, бұл хабарлама шығарылмайды, бұл қате жасырылады. Белгілі бір архитектуралар мен дерек үлгілерінде (мысалы, 64 биттік жүйелердегі LP64, онда long және көрсеткіштер 64 биттік, ал int 32 биттік болады), бұл қате нақты анықталмаған мінез-құлыққа алып келуі мүмкін, себебі жасырын түрде жарияланған malloc 32 биттік мәнді қайырады, ал нақты анықталған функция 64 биттік мәнді қайырады. Шақыру конвенцияларына және жад орналасуына байланысты, бұл стекке зиян келтіруі мүмкін. Бұл мәселе заманауи компиляторларда көзден жасырынуы ықтимал емес, себебі C99 жасырын жариялауға рұқсат бермейді, сондықтан компилятор диагностикалық хабарлама беруі керек, тіпті ол int типті мән қайырады деп есептесе де. Егер көрсеткіштің типі оның жариялануында өзгертілсе, malloc шақырылған және түрлендірілген барлық жолдарды да өзгерту қажет болуы мүмкін.
Қолданылу
Жад басқаруды іске асыру операциялық жүйеге және архитектураға үлкен дәрежеде байланысты. Кейбір операциялық жүйелер malloc үшін бөліп берушімен қамтамасыз етеді, ал басқалары деректердің белгілі бір аймақтарын басқаруға арналған функцияларды ұсынады. C++ тіліндегі malloc және `new` операторларын іске асыру үшін көбінесе бір және сол динамикалық жадты бөліп беруші қолданылады.
dlmalloc және ptmalloc
Даг Ли 1987 жылдан бастап жалпы мақсаттағы бөлгіш ретінде dlmalloc ("Doug Lea's Malloc") қоғамдық доменде әзірледі. GNU C кітапханасы (glibc) Wolfram Gloger-дің ptmalloc ("pthreads malloc") бағдарламасынан туындаған, ол dlmalloc-тың түйіндемесі болып табылады және жиектермен жұмыс істеуге қатысты жақсартулар енгізген. 2023 жылдың қараша айына дейін dlmalloc-тың соңғы нұсқасы 2012 жылдың тамызынан бері 2.8.6 нұсқасы болып табылады.
dlmalloc – шекаралық белгілерді пайдаланатын бөлгіш. Жинақтағы жад "бөліктер" ретінде бөлінеді, бұл 8 байтқа сәйкес келтірілген дерек құрылымы, онда бас және пайдалануға болатын жад орналасқан. Бөлінген жадта бөлік мөлшері мен пайдалану белгілері үшін 8 немесе 16 байттық қосымша кеңістік қарастырылған (доп векторына ұқсас). Бөлінбеген бөліктер де пайдалануға болатын кеңістіктегі басқа бос бөліктерге сілтемелерді сақтайды, бұл 32 биттік жүйелерде ең кішкентай бөлік өлшемін 16 байтқа, ал 64 биттік жүйелерде 24/32 байтқа (туралауға байланысты) дейін арттырады. mmap әдісі үлкен буферлердің мерзімі өткеннен кейін кішкентай бөлікті ұстап қалуына байланысты туындайтын мәселелерді болдырмайды, бірақ әрқашан жадтың толық бетін бөледі, көптеген архитектураларда бұл 4096 байтқа тең. Ойын әзірлеушісі Адриан Стоун, шекаралық белгілерді пайдаланатын бөлгіш ретінде, виртуалды жады бар, бірақ сұраныс бойынша беттерді алмастырмайтын консольдік жүйелер үшін ыңғайсыз деп санайды. Себебі, оның жинақтың қысқаруы және өсуіне байланысты шақырулары (/) виртуалды жадтың жеке беттерін бөлу және бекіту үшін қолданылмайды. Сұраныс бойынша беттерді алмастыру болмаған жағдайда фрагментация көбірек алаңдау тудырады.
FreeBSD және NetBSD jemalloc-ы
FreeBSD 7.0 және NetBSD 5.0 нұсқаларынан бастап, ескі malloc жүйесі (Пуль Хеннинг Камп жасаған) Джейсон Эванс жазған jemalloc жүйесімен алмастырылды. Бұл өзгерістің басты себебі – көп тірістік ортадағы кеңейтімге қабілетсіздігі. Құлыптасу мәселесін болдырмау үшін, jemalloc әрбір процессор үшін жеке "ареналарды" пайдаланады. Көп тірістік қолданбаларда секундына жасалған бөлулер санын өлшеу бойынша жүргізілген тәжірибелер, jemalloc-тың тірістер санына пропорционал сызықтық кеңейтімге қол жеткізетінін көрсетті, ал phkmalloc және dlmalloc жүйелерінің өнімділігі тірістер санына кері пропорционалды болды.
OpenBSD-нің қатесі
OpenBSD-де malloc функциясының жұмыс істеуі mmap-ты пайдаланады. Бір беттен асып түсетін сұраныстар үшін, барлық бөлінген жад mmap арқылы алынады; кішірек көлемдер mmap арқылы бөлінген «бакет беттердегі» malloc жүйесінде сақталатын жад қоймаларынан тағайындалады. Жадыны босату кезінде, munmap функциясы арқылы жад босатылып, процесс адрестік кеңістігінен алып тасталады. Бұл жүйе OpenBSD-нің mmap жүйелік шақыруының құрамында жүзеге асырылған адрестік кеңістіктің кездейсоқ орналасуы және бос беттер мүмкіндіктерін пайдаланып, қауіпсіздікті арттыруға, сондай-ақ жады босатылғаннан кейін оны қайта пайдалану қателерін анықтауға бағытталған. Үлкен жад бөлігі босатылғаннан кейін толығымен алып тасталғандықтан, оны әрі қарай пайдалану сегментация қатесіне және бағдарламаның тоқтауына себеп болады. GrapheneOS жобасы бастапқыда OpenBSD-нің жад бөлушісін Android-тің Bionic C кітапханасына көшіруден басталды.
Маллокты қорлау
Хоард – масштабталатын жад бөлу өнімділігіне қол жеткізуді мақсат ететін жадты бөлуші. OpenBSD-нің жадты бөлушісі сияқты, Хоард тек mmap-ты пайдаланады, бірақ жадты 64 килобайттық суперблоктарға бөліп басқарады. Хоардтың жады логикалық тұрғыдан бір жаһандық жад және процессорлар бойынша бірнеше жадқа бөлінеді. Сонымен қатар, шектеулі мөлшерде суперблоктарды сақтауға арналған жинақтағыш кэш бар. Хоард тек локальды жинақтағышқа немесе процессорлық жадқа суперблоктардан жад бөліп, көбінесе бос суперблоктарды жаһандық жадқа жылжыту арқылы, оларды басқа процессорлар қайта пайдалануына мүмкіндік береді. Бұл жадтың фрагментациясын төмен ұстап, жинақтар санымен дерлік сызықтық масштабтауға қол жеткізеді.
мималлока
Microsoft Research-тің өнімділікке баса назар беретін, жалпы мақсаттағы, ашық бастапқы кодты, ықшам жадты бөлу құралы. Кітапхана шамамен 11 000 код жолынан тұрады.
Сымды кэштеуші малок (tcmalloc)
Әрбір жіпте кішкентай бөлулер үшін жіпке арналған жергілікті сақтау орны бар. Үлкен бөлулер үшін mmap немесе sbrk қолданылуы мүмкін. Google әзірлеген TCMalloc, өлі жіптердің жергілікті сақтауы үшін қоқыс жинауға ие. TCMalloc көп жіпті бағдарламаларда glibc-тің ptmalloc-інен екі еседен астам жылдам деп есептеледі.
Ішіндегі ядро
Операциялық жүйе ядроларына жадты бөлу қажет, сияқты қолданбалы бағдарламаларға да. Дегенмен, ядродағы malloc іске асырылуы C кітапханаларында қолданылатын нұсқалардан едәуір өзгеше болуы мүмкін. Мысалы, жад буферлері DMA-ның ерекше талаптарына сай болуы керек, немесе жадты бөлу функциясы үзіліс ортасынан шақырылуы ықтимал. Бұл операциялық жүйе ядросының виртуалды жад кіші жүйесімен тығыз байланысты malloc іске асырылуын талап етеді.
Басты малок
Malloc және оның туыс функциялары бағдарламаның өнімділігіне елеулі әсер ете алатындықтан, нақты қолданба үшін осы функцияларды қолданбаның жадты бөлу ерекшеліктеріне оңтайландырылған жекелеген нұсқалармен алмастыру жиі кездеседі. C стандарты мұндай мүмкіндік қарастырмайды, бірақ операциялық жүйелер динамикалық байланыс арқылы осыны іске асырудың әртүрлі жолдарын тапқан. Бір жолы – символдарды қайта анықтау үшін басқа кітапхананы қосу. Unix System V.3 жүйесі қолданған тағы бір тәсіл – malloc және free функцияларын қолданбалар өздеріне қажетті функцияларға қайта бағдарлай алатын функция көрсеткіштері ретінде жасау.
Ұзартулар мен баламалар
C кітапханасының әртүрлі операциялық жүйелермен және компиляторлармен бірге келетін нұсқалары стандартты malloc интерфейсіне баламалар мен кеңейтулерді қамтуы мүмкін. Олардың ішінде: alloca, ол шақыру стегінде сұралған байттар санын бөліп береді. Оған сәйкес жадты босату функциясы жоқ, себебі әдетте жад шақырушы функция қайтарылған кезде босатылады. alloca Unix жүйелерінде 32/V (1978) нұсқасынан бастап қолданылып келген, бірақ оны пайдалану кейбір (мысалы, кіріктірілген) жағдайларда қиындықтар тудыруы мүмкін. Көптеген компиляторлар қолдаса да, ол ANSI C стандартының бөлігі емес, сондықтан әрқашан портативті болмауы мүмкін. Бұл аз ғана өнімділік мәселелерін тудыруы мүмкін: өйткені өзгермелі өлшемді стек фреймдер пайда болады, сондықтан стек және фрейм көрсеткіштерін басқару қажет (белгілі өлшемді стек фреймдерде олардың бірі артық). Ірі бөлулер стек ағып кетуіне байланысты белгісіз мінез-құлық қаупін арттыруы мүмкін. C99 өзгермелі ұзындығы бар массивтерді стекке жадты бөлудің баламалы механизмі ретінде ұсынды, бірақ бұл мүмкіндік кейінірек C11 стандартында міндетті емес опция ретінде қалды. POSIX posix_memalign функциясын анықтайды, ол шақырушы белгілеген сәйкестік бойынша жадты бөліп береді. Оның бөлінген жады free функциясымен босатылады, сондықтан оның іске асырылуы malloc кітапханасының бір бөлігі болуы керек.
alloca, which allocates a requested number of bytes on the call stack. No corresponding deallocation function exists, as typically the memory is deallocated as soon as the calling function returns. alloca was present on Unix systems as early as 32/V (1978), but its use can be problematic in some (e. g., embedded) contexts. While supported by many compilers, it is not part of the ANSI C standard and therefore may not always be portable. It may also cause minor performance problems: it leads to variable size stack frames, so that both stack and frame pointers need to be managed (with fixed size stack frames, one of these is redundant). Larger allocations may also increase the risk of undefined behavior due to a stack overflow. C99 offered variable length arrays as an alternative stack allocation mechanism however, this feature was relegated to optional in the later C11 standard. POSIX defines a function posix memalign that allocates memory with caller specified alignment. Its allocations are deallocated with free, so the implementation usually needs to be a part of the malloc library.