0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

什么是堆內(nèi)存?堆內(nèi)存是如何分配的?

電子工程師 ? 來源:?jiǎn)纹瑱C(jī)匠人 ? 作者:蔡琰老師 ? 2021-07-05 17:58 ? 次閱讀

上一篇我們分享了棧內(nèi)存的概念,現(xiàn)在我們分享下堆內(nèi)存的概念。

在一般的編譯系統(tǒng)中,堆內(nèi)存的分配方向和棧內(nèi)存是相反的。當(dāng)棧內(nèi)存從高地址向低地址增長(zhǎng)的時(shí)候,堆內(nèi)存從低地址向高地址分配。

C語言中,堆內(nèi)存在分配和釋放的時(shí)候,是程序通過調(diào)用C語言的庫函數(shù)完成的。這和棧內(nèi)存的分配有區(qū)別,棧內(nèi)存利用的是處理器的硬件機(jī)制,而堆內(nèi)存的處理使用的是庫函數(shù)。

我們來看下堆內(nèi)存的分配情況:

在堆內(nèi)存的分配過程中,每次分配將返回一個(gè)當(dāng)前分配地址的指針。在程序中如果多次分配內(nèi)存,可以得到多個(gè)內(nèi)存指針,每個(gè)內(nèi)存指針都是本次分配內(nèi)存的地址。在釋放內(nèi)存的時(shí)候,只需要對(duì)每個(gè)指針進(jìn)行操作,那個(gè)指針?biāo)赶虻膬?nèi)存就會(huì)被釋放,而對(duì)其他的內(nèi)存區(qū)域沒有影響。

從內(nèi)存的分配和使用上,可以看出棧內(nèi)存和堆內(nèi)存的區(qū)別:棧內(nèi)存只有一個(gè)入口點(diǎn),就是棧指針,棧內(nèi)存壓入和彈出的時(shí)候棧指針將發(fā)生變化,棧指針標(biāo)識(shí)當(dāng)前棧區(qū)域中已使用和未使用的界限,程序在訪問棧內(nèi)存的時(shí)候都只能通過棧指針及其偏移量;而堆內(nèi)存有多個(gè)入口點(diǎn),每次分配得到的指針是訪問內(nèi)存的入口,每個(gè)分配內(nèi)存區(qū)域都可以被單獨(dú)釋放,程序?qū)Χ褍?nèi)存可以通過每次分配得到的指針訪問。

堆內(nèi)存有一個(gè)整體分配的過程,按照向上的堆內(nèi)存分配方向。隨著堆內(nèi)存使用量的增加,堆內(nèi)存將逐漸向高地址分配。這只是一個(gè)大體的增長(zhǎng)的方面,在堆內(nèi)存中,已使用的區(qū)域和未使用的區(qū)域是交錯(cuò)的,而不是像棧區(qū)域那樣有明顯的分界線。

堆內(nèi)存的釋放看下面這個(gè)圖:

看到這樣頻繁的使用區(qū)域和釋放,那么很容易看出堆內(nèi)存是不連續(xù)的,跟堆內(nèi)存的使用方式有關(guān)系,這個(gè)分配就相對(duì)自由靈活了,但是也是會(huì)在低地址向高地址發(fā)展的方向分配的。

比如上面釋放后再分配就可以是下面兩種情況:

先看再次分配1的情況:當(dāng)新分配的需求比中間(剛剛釋放)區(qū)域小,那么就會(huì)在緊接著的區(qū)域給分配。

再看再次分配2的情況:當(dāng)新分配的需求比中間(釋放的)區(qū)域大,那么只能往后尋求能給的區(qū)域。

當(dāng)頻繁的分配和釋放內(nèi)存的過程中,會(huì)很容易出現(xiàn)在兩塊已經(jīng)分配的內(nèi)存之間較小的未分配內(nèi)存區(qū)域,這些其實(shí)可以用,但是由于他們的空間比較小,不夠連續(xù)內(nèi)存的分配,所以分配的時(shí)候就很難再次使用,這些較小的內(nèi)存就是我們常說的內(nèi)存碎片。

我們?cè)賮砹囊幌略贑程序中堆空間的使用。

在C語言中,堆內(nèi)存區(qū)域的分配和釋放是通過調(diào)用庫函數(shù)來完成的,實(shí)現(xiàn)的函數(shù)主要有四個(gè):

void *malloc(size_t size); //分配內(nèi)存空間

void free(void *ptr); //釋放內(nèi)存空間

void *calloc(size_t nmemb,size_t size); //分配內(nèi)存空間

void *realloc(void * ptr,size_t size); //重新分配內(nèi)存空間

注意:使用上面這幾個(gè)函數(shù)需要包含標(biāo)準(zhǔn)庫文件

那么庫函數(shù)怎么使用呢,內(nèi)存分配了就要有釋放,那么常用的就是malloc()和free()兩個(gè)函數(shù)。malloc()函數(shù)的輸入是需要分配內(nèi)存的大小,輸出是分配內(nèi)存的指針。如果分配不成功,則返回NULL。

free()函數(shù)的輸入是需要釋放的指針,可以接受任何形式的指針。這個(gè)指針必須是由分配函數(shù)分配出來的。

例如:

int *pa;

pa = (int *)malloc(sizeof(int));//分配一個(gè)int大小的指針

if(NULL != pa)

{

free(pa);

}

內(nèi)存使用完成需要釋放,以便分配給其他程序使用。

calloc()也是內(nèi)存分配的,只是可以把分配好的內(nèi)存區(qū)域的初始值全部設(shè)置為0。還有這個(gè)分配內(nèi)存有兩個(gè)參數(shù),第一個(gè)是分配單元的大小,第二個(gè)是要分配的數(shù)目。

malloc(sizeof(unsigned int)*10); == calloc(sizeof(unsigned int),10)

realloc()有兩個(gè)參數(shù),一個(gè)是指向內(nèi)存的地址指針,一個(gè)是要重分配內(nèi)存的大小,返回值是指向所分配內(nèi)存的指針。

1、當(dāng)參數(shù)指針為NULL的時(shí)候,作為malloc使用,分配內(nèi)存。

2、當(dāng)重分配內(nèi)存大小為0的時(shí)候,作為free使用,釋放內(nèi)存。

3、當(dāng)指針和重分配內(nèi)存大小均不為0的時(shí)候,根據(jù)指針指向的堆內(nèi)存區(qū)域的情況和指針大小重新分配內(nèi)存。

對(duì)于realloc()作為重新分配內(nèi)存的時(shí)候,有三種可能出現(xiàn):

1、縮小內(nèi)存

2、擴(kuò)大內(nèi)存,不需要移動(dòng)指針

3、擴(kuò)大內(nèi)存,需要移動(dòng)指針(指定內(nèi)存區(qū)域大小不夠)

在堆內(nèi)存的管理上,主要容易出現(xiàn)以下幾個(gè)問題:

1、開辟的內(nèi)存沒有釋放,造成內(nèi)存泄漏(系統(tǒng)不會(huì)釋放任何用戶分配的內(nèi)存)

2、野指針被使用或釋放(內(nèi)存釋放后,需要將內(nèi)存指針置為NULL)

3、非法釋放指針(分配了有效內(nèi)存才存在釋放,否則是非法的)

在C語言語法的方面對(duì)棧內(nèi)存和堆內(nèi)存如何使用沒有限制。然后從使用的角度,棧內(nèi)存更適用于容量較小的單個(gè)變量(例如:C語言的基本變量類型、較小的結(jié)構(gòu)體和數(shù)組),堆內(nèi)存則適用于開辟較大塊的內(nèi)存。棧內(nèi)存由編譯器分配和釋放,堆內(nèi)存由程序員分配和釋放。

責(zé)任編輯:lq6

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    2942

    瀏覽量

    73728
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7581

    瀏覽量

    135547

原文標(biāo)題:堆內(nèi)存的那些事

文章出處:【微信號(hào):gh_e7f294a514ca,微信公眾號(hào):?jiǎn)纹瑱C(jī)匠人】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    轉(zhuǎn)載 golang內(nèi)存分配

    Go 的分配采用了類似 tcmalloc 的結(jié)構(gòu).特點(diǎn): 使用一小塊一小塊的連續(xù)內(nèi)存頁, 進(jìn)行分配某個(gè)范圍大小的內(nèi)存需求. 比如某個(gè)連續(xù) 8KB 專門用于
    的頭像 發(fā)表于 09-05 14:12 ?163次閱讀
    轉(zhuǎn)載 golang<b class='flag-5'>內(nèi)存</b><b class='flag-5'>分配</b>

    如何使用SystemView的監(jiān)控功能

    SystemView能夠監(jiān)視應(yīng)用程序如何使用動(dòng)態(tài)存儲(chǔ)。這意味著,如果應(yīng)用程序中使用了C或C++、自定義或RTOS提供的內(nèi)存池對(duì)象,我們可以跟蹤這些對(duì)象的使用情況。SystemView可以在一個(gè)
    的頭像 發(fā)表于 08-09 18:07 ?642次閱讀
    如何使用SystemView的<b class='flag-5'>堆</b>監(jiān)控功能

    C語言內(nèi)存泄漏問題原理

    內(nèi)存泄漏問題只有在使用內(nèi)存的時(shí)候才會(huì)出現(xiàn),棧內(nèi)存不存在內(nèi)存泄漏問題,因?yàn)闂?b class='flag-5'>內(nèi)存會(huì)自動(dòng)
    發(fā)表于 03-19 11:38 ?413次閱讀
    C語言<b class='flag-5'>內(nèi)存</b>泄漏問題原理

    拆解mmap內(nèi)存映射的本質(zhì)!

    mmap 內(nèi)存映射里所謂的內(nèi)存其實(shí)指的是虛擬內(nèi)存,在調(diào)用 mmap 進(jìn)行匿名映射的時(shí)候(比如進(jìn)行內(nèi)存
    的頭像 發(fā)表于 01-24 14:30 ?1150次閱讀
    拆解mmap<b class='flag-5'>內(nèi)存</b>映射的本質(zhì)!

    和棧的區(qū)別和使用注意事項(xiàng)

    和棧是在計(jì)算機(jī)科學(xué)中廣泛使用的兩種數(shù)據(jù)結(jié)構(gòu),它們具有不同的用途和特點(diǎn)。和棧的區(qū)別涉及到內(nèi)存分配、訪問方式、數(shù)據(jù)存儲(chǔ)等方面。在使用和棧時(shí)
    的頭像 發(fā)表于 01-18 17:24 ?1887次閱讀

    glibc malloc內(nèi)存分配器的實(shí)現(xiàn)原理

    內(nèi)存(Heap Memory)是一個(gè)很有意思的領(lǐng)域。你可能和我一樣,也困惑于下述問題很久了。
    的頭像 發(fā)表于 01-17 10:03 ?752次閱讀
    glibc malloc<b class='flag-5'>內(nèi)存</b><b class='flag-5'>分配</b>器的實(shí)現(xiàn)原理

    嵌入式軟件內(nèi)存與指針相關(guān)問題

    隱性的內(nèi)存泄露問題 內(nèi)存泄漏(Memory Leak)是指程序中已動(dòng)態(tài)分配內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)
    的頭像 發(fā)表于 12-07 16:07 ?366次閱讀

    jvm配置內(nèi)存初始值參數(shù)

    JVM(Java Virtual Machine)是Java語言的運(yùn)行環(huán)境,它通過解釋字節(jié)碼并執(zhí)行相應(yīng)的指令來運(yùn)行Java程序。在JVM中,(Heap)是用于存儲(chǔ)對(duì)象實(shí)例的內(nèi)存區(qū)域。而在Java
    的頭像 發(fā)表于 12-05 14:17 ?677次閱讀

    java虛擬機(jī)內(nèi)存包括遠(yuǎn)空間內(nèi)存

    詳細(xì)介紹JVM內(nèi)存的各個(gè)部分及其作用。 Java(Heap) Java是JVM管理的最大一塊內(nèi)存區(qū)域,用于存放Java對(duì)象實(shí)例。在
    的頭像 發(fā)表于 12-05 14:15 ?347次閱讀

    jvm內(nèi)存區(qū)域由哪幾部分組成

    JVM(Java Virtual Machine)是Java程序運(yùn)行的環(huán)境,在JVM中存在著多個(gè)不同功能的內(nèi)存區(qū)域。這些內(nèi)存區(qū)域可以被分為幾個(gè)部分,包括內(nèi)存、棧
    的頭像 發(fā)表于 12-05 14:10 ?722次閱讀

    jvm管理的內(nèi)存包括哪幾個(gè)運(yùn)行時(shí)數(shù)據(jù)內(nèi)存

    詳細(xì)介紹每個(gè)運(yùn)行時(shí)數(shù)據(jù)內(nèi)存的作用和特點(diǎn)。 內(nèi)存(Heap Memory): 內(nèi)存是JVM管理的最大的一塊
    的頭像 發(fā)表于 12-05 14:09 ?471次閱讀

    jmap dump內(nèi)存的命令是

    jmap dump是Java內(nèi)存映像工具(Java Memory Map Tool)的一個(gè)功能,用于生成Java虛擬機(jī)(JVM)中的內(nèi)存快照。內(nèi)
    的頭像 發(fā)表于 12-05 10:38 ?2876次閱讀

    java內(nèi)存溢出排查方法

    Java內(nèi)存溢出(Memory overflow)是指Java虛擬機(jī)(JVM)中的內(nèi)存無法滿足對(duì)象分配的需求,導(dǎo)致程序拋出OutOfMemoryError異常。
    的頭像 發(fā)表于 11-23 14:46 ?2833次閱讀

    malloc 申請(qǐng)內(nèi)存的兩種方式

    我們知道m(xù)alloc() 并不是系統(tǒng)調(diào)用,也不是運(yùn)算符,而是 C 庫里的函數(shù),用于動(dòng)態(tài)分配內(nèi)存。 malloc 申請(qǐng)內(nèi)存的時(shí)候,會(huì)有兩種方式向操作系統(tǒng)申請(qǐng)
    的頭像 發(fā)表于 11-13 11:42 ?2413次閱讀
    malloc 申請(qǐng)<b class='flag-5'>內(nèi)存</b>的兩種方式

    程序內(nèi)存分區(qū)中的與棧

    與棧表示兩種內(nèi)存管理方式; (2)數(shù)據(jù)結(jié)構(gòu)場(chǎng)景下,與棧表示兩種常用的數(shù)據(jù)結(jié)構(gòu)。 1.程序內(nèi)存分區(qū)中的與棧 1.1 棧簡(jiǎn)介 棧由操作系統(tǒng)自動(dòng)分配
    的頭像 發(fā)表于 11-11 16:21 ?684次閱讀
    程序<b class='flag-5'>內(nèi)存</b>分區(qū)中的<b class='flag-5'>堆</b>與棧