精华内容
下载资源
问答
  • 进程的内存分配情况

    千次阅读 2011-10-29 23:05:41
    进程地址空间中典型存储区域分配情况如下图所示(出自《UNIX环境高级编程》):   从图中可以看出: 从低地址到高地址分别内存区分别为: 代码段 数据段(初始化) 数据段(未初始化)(BSS) ...

     本文出自:http://blog.csdn.net/hongchangfirst

    进程地址空间中典型的存储区域分配情况如下图所示(出自《UNIX环境高级编程》):

     

    从图中可以看出:

    从低地址到高地址分别内存区分别为:

    1. 代码段

    2. 数据段(初始化)

    3. 数据段(未初始化)(BSS)

    4. 命令行参数和环境变量

    其中,堆向高内存地址生长,栈向低内存地址生长。

     

    在进程的地址空间中:

    1. 代码段中存放:全局常量(const)、字符串常量、函数以及编译时可决定的某些东西

    2. 数据段(初始化)中存放:初始化的全局变量、初始化的静态变量(全局的和局部的)

    3. 数据段(未初始化)(BSS)中存放:未初始化的全局变量、未初始化的静态变量(全局的和局部的)

    4. 堆中存放:动态分配的区域(malloc、new等)

    5. 栈中存放:局部变量(初始化以及未初始化的,但不包含静态变量)、局部常量(const)

    6. 命令行参数和环境变量顾名思义存放命令行参数和环境变量微笑

     

    奋斗

    展开全文
  • C中进程的内存分配

    千次阅读 2013-05-19 17:39:31
     stack: 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间。  heap: 需要程序员自己申请,并指明大小,在C中malloc函数,C++中是new运算符。  如p1 = (char *)malloc(10); ...



    堆和栈的比较:

    1,申请方式
      stack: 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间。
      heap: 需要程序员自己申请,并指明大小,在C中malloc函数,C++中是new运算符。
      如p1 = (char *)malloc(10); p1 = new char[10];
      如p2 = (char *)malloc(10); p2 = new char[20];
      但是注意p1、p2本身是在栈中的。

    2,申请后系统的响应
      栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
      堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
      对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
      由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
    3,申请大小的限制
      栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因 此,能从栈获得的空间较小。
      堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
    4,申请效率的比较
      栈由系统自动分配,速度较快。但程序员是无法控制的。
      堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。
      另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是栈,而是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。
    5,堆和栈中的存储内容
      栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
      当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
      堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
    6,存取效率的比较
      char s1[] = "a";
      char *s2 = "b";
      a是在运行时刻赋值的;而b是在编译时就确定的;但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 比如:

    1. int main()  
    2. {  
    3.     char a = 1;  
    4.   char c[] = "1234567890";  
    5.   char *p ="1234567890";  
    6.   a = c[1];  
    7.   a = p[1];  
    8.     
    9.     return 0;  
    10. }  

      对应的汇编代码
      10: a = c[1];
      00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
      0040106A 88 4D FC mov byte ptr [ebp-4],cl
      11: a = p[1];
      0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
      00401070 8A 42 01 mov al,byte ptr [edx+1]
      00401073 88 45 FC mov byte ptr [ebp-4],al
      第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,再根据edx读取字符,显然慢了。
    7,小结
      堆和栈的主要区别由以下几点:
      1、管理方式不同;
      2、空间大小不同;
      3、能否产生碎片不同;
      4、生长方向不同;
      5、分配方式不同;
      6、分配效率不同;
      管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
      空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。当然,这个值可以修改。
      碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构。
      生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
      分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由malloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
      分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分 到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
      从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址, EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。
      虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。
      无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果。

    展开全文
  • 分类: Linux内核设计艺术 2013...内存内存分配mallocC行业数据 如何查看进程发生缺页中断次数?  用ps -o majflt,minflt -C program命令查看。  majflt代表major fault,中文名叫大错误,minflt代表m

    如何查看进程发生缺页中断的次数

             用ps -o majflt,minflt -C program命令查看。

              majflt代表major fault,中文名叫大错误,minflt代表minor fault,中文名叫小错误

              这两个数值表示一个进程自启动以来所发生的缺页中断的次数

    发成缺页中断后,执行了那些操作?

    当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作: 
    1、检查要访问的虚拟地址是否合法 
    2、查找/分配一个物理页 
    3、填充物理页内容(读取磁盘,或者直接置0,或者啥也不干) 
    4、
    建立映射关系(虚拟地址到物理地址) 
    重新执行发生缺页中断的那条指令 
    如果第3步,需要读取磁盘,那么这次缺页中断就是majflt,否则就是minflt。 

    内存分配的原理

    从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。

    1、brk是将数据段(.data)的最高地址指针_edata往高地址推;

    2、mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存

         这两种方式分配的都是虚拟内存,没有分配物理内存在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。


    在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap,munmap这些系统调用实现的。


    下面以一个例子来说明内存分配的原理:

    情况一、malloc小于128k的内存,使用brk分配内存,将_edata往高地址推(只分配虚拟空间,不对应物理内存(因此没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系),如下图:


    1、进程启动的时候,其(虚拟)内存空间的初始布局如图1所示。
          其中,mmap内存映射文件是在堆和栈的中间(例如libc-2.2.93.so,其它数据文件等),为了简单起见,省略了内存映射文件。
          _edata指针(glibc里面定义)指向数据段的最高地址。 
    2、
    进程调用A=malloc(30K)以后,内存空间如图2:
          malloc函数会调用brk系统调用,将_edata指针往高地址推30K,就完成虚拟内存分配。
          你可能会问:只要把_edata+30K就完成内存分配了?
          事实是这样的,_edata+30K只是完成虚拟地址的分配,A这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配了A这块内容,然后从来不访问它,那么,A对应的物理页是不会被分配的。 
    3、
    进程调用B=malloc(40K)以后,内存空间如图3。

    情况二、malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空闲内存分配(对应独立内存,而且初始化为0),如下图:


    4、进程调用C=malloc(200K)以后,内存空间如图4:
          默认情况下,malloc函数分配内存,如果请求内存大于128K(可由M_MMAP_THRESHOLD选项调节),那就不是去推_edata指针了,而是利用mmap系统调用,从堆和栈的中间分配一块虚拟内存
          这样子做主要是因为::
          brk分配的内存需要等到高地址内存释放以后才能释放(例如,在B释放之前,A是不可能释放的,这就是内存碎片产生的原因,什么时候紧缩看下面),而mmap分配的内存可以单独释放。
          当然,还有其它的好处,也有坏处,再具体下去,有兴趣的同学可以去看glibc里面malloc的代码了。 
    5、进程调用D=malloc(100K)以后,内存空间如图5;
    6、进程调用free(C)以后,C对应的虚拟内存和物理内存一起释放。

    7、进程调用free(B)以后,如图7所示:
            B对应的虚拟内存和物理内存都没有释放,因为只有一个_edata指针,如果往回推,那么D这块内存怎么办呢
    当然,B这块内存,是可以重用的,如果这个时候再来一个40K的请求,那么malloc很可能就把B这块内存返回回去了。 
    8、进程调用free(D)以后,如图8所示:
            B和D连接起来,变成一块140K的空闲内存。
    9、默认情况下:
           当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩,变成图9所示。
    展开全文
  • Delphi 为我们提供了三个方便函数:   GlobalAllocPtr {简化自 API GlobalAlloc} GlobalReAllocPtr {简化自 API GlobalReAlloc} GlobalFreePtr {简化自

     Delphi 为我们提供了三个方便的函数:

     

    GlobalAllocPtr   {简化自 API 的 GlobalAlloc}


    GlobalReAllocPtr {简化自 API 的 GlobalReAlloc}

    GlobalFreePtr    {简化自 API 的 GlobalFree}


    读写本程序以外的数据时可以使用它们, 很方便, 譬如:

    p := GlobalAllocPtr(0, Len);      {分配}

    p := GlobalReAllocPtr(p, Len, 0); {重分配}

    GlobalFreePtr(p);                 {释放}


    注意 GlobalAllocPtr 的第一个参数和 GlobalReAllocPtr 的最后一个参数, 上面给的都是 0;


    这两个参数的意义是一样的, 规范一点应该写成 GMEM_FIXED (表示分配固定内存), 常用的参数还有:

     

    GMEM_MOVEABLE {分配可移动内存}

    GMEM_ZEROINIT {同时清空内存}

    GHND          {分配可移动内存同时清空}

    GPTR          {分配固定内存同时清空}


    参数还有很多, 和相应的 API 函数的参数是一致的.

    下面的例子并没有使用进程以外的对象, 可以把 Memo1.Handle 换做外部的其他文本容器(譬如记事本)试一试:



    代码文件:


    unit Unit1;


    interface


    uses

      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

      Dialogs, StdCtrls;


    type

      TForm1 = class(TForm)

        Memo1: TMemo;

        Button1: TButton;

        Button2: TButton;

        procedure FormCreate(Sender: TObject);

        procedure Button1Click(Sender: TObject);

        procedure Button2Click(Sender: TObject);

      end;


    var

      Form1: TForm1;


    implementation


    {$R *.dfm}


    procedure TForm1.Button1Click(Sender: TObject);

    var

      p: Pointer;

      Len: Integer;

    begin

      Len := 6+1;                   {假如想要读出 6 个字符, 要留出结束的空字符}

      p := GlobalAllocPtr(0, Len*2);{分配内存 Len*2 是针对双字节字符}

      SendMessage(Memo1.Handle, WM_GETTEXT, Len, Cardinal(p));

      ShowMessage(PChar(p)); {CodeGe}

      GlobalFreePtr(p);

    end;


    procedure TForm1.Button2Click(Sender: TObject);

    var

      p: Pointer;

      Len: Integer;

    begin

      Len := 6+1;                   {假如想要读出 6 个字符, 要流出结束的空字符}

      p := GlobalAllocPtr(0, Len*2);{分配内存 Len*2 是针对双字节字符}

      SendMessage(Memo1.Handle, WM_GETTEXT, Len, Cardinal(p));

      ShowMessage(PChar(p)); {CodeGe}

     

      {在上一例的基础上继续, 先获取实际长度}

      Len := SendMessage(Memo1.Handle, WM_GETTEXTLENGTH,0, 0);

      Len := (Len + 1) * 2;

      p := GlobalReAllocPtr(p, Len, GHND); {重新分配内存}

      SendMessage(Memo1.Handle, WM_GETTEXT, Len, Cardinal(p));

      ShowMessage(PChar(p)); {CodeGear Delphi 2009}


      GlobalFreePtr(p);

    end;

    procedure TForm1.FormCreate(Sender: TObject);

    begin
      Memo1.Align := alLeft;

      Memo1.ScrollBars := ssVertical;

      Memo1.Text := 'CodeGear Delphi 2009';


    end;



    end.

    展开全文
  • 进程概述和内存分配

    万次阅读 2016-06-16 17:28:08
    一个进程的内存布局是什么样的? 每个进程所所分配的内存由很多部分组成,通常我们称之为段,一般会有如下段: 文本段 包含了进程执行的程序机器语言指令,文本段具有只读属性,以防止进程通过错误指针意外修改自身...
  • 因为最近要解决网页经常崩溃的问题,所以去研究了一下chrome浏览器的标签页和进程的关系,以及标签页对应进程的内存分配。 标签页和进程的对应关系 当chrome浏览器开启但是没有任何一个标签页的时候,打开Activity ...
  • 这张图中有映射段的位置,但是还有一个重要的部分的缺失,就是运行时的参数和环境变量,在 Linux/Unix 系统编程手册这本书第 6 章讲进程的内存分配里有给: malloc 内存分配在映射段 当 malloc 申请分配的内存过大...
  • Linux进程内存分配

    千次阅读 2018-12-21 17:43:50
    从内核角度来看,进程由用户内存空间和一系列内核数据结构组成,其中用户内存空间包含程序代码及代码所使用变量(程序段和数据段),而内核数据结构则用于维护进程状态信息。  每个进程都有一个进程号,用以...
  • 进程空间内存分配详解

    千次阅读 2019-08-20 11:50:00
    进程空间的内存分配 从高地址到低地址如图所示: 名称 操作系统内核区 用户不可见 用户栈 栈指针,向下扩展 动态堆 向上扩展 全局区(静态区) .data初始化.bss未初始化 文字常量区(只读数据) ...
  • 进程内存分配的时候,oep和mep的关系是什么,程序的入口点和堆分配表有关系么?怎么得到当前的内存页?
  • 上一节大致讲了下系统是如何为进程分配内存。那么一个进程得到了这4G空间后该怎么办呢?程序员可以使用全部内存么?答案是否定的。...A进程地址0X400000(4M)映射了可执行文件,而B进程的地址0X400
  • 进程内存分配

    千次阅读 2017-04-24 16:19:24
    进程内存分配图 高地址                           低地址 段区名 解释 内容 stack 栈:局部变量 1在函数内部定义局部变量(非static...
  • 在为多个进程分配内存空间时,由于内存的大小固定,所以如何分?分多少?量不够?等问题都会影响到内存的利用效率 涉及三个问题: 1)最小物理块确定,一个进程有多个页组成,在内存中完全存储有可能需要很多个...
  • 进程内存的分配

    2014-08-21 14:33:13
    进程内存的分配主要有5个部分,打
  • 进程虚拟内存分配

    千次阅读 2012-07-28 11:13:03
    进程的虚拟地址空间,堆栈、堆、数据段、代码段 上图是进程的虚拟地址空间示意图。 堆栈段:  1. 为函数内部的局部变量提供存储空间。  2. 进行函数调用时,存储“过程活动记录”。  3. 用作暂时存储区...
  • Android进程内存分配

    千次阅读 2016-12-13 14:59:23
    最近在网上看了不少Android内存管理方面博文,但是文章大多都是就单个方面去介绍内存管理,没有能全局把握,缺乏系统性阐述,而且有些观点有误。 这样对Android内存管理进行局部性介绍,很难使读者建立系统性概念...
  • 如何查看进程发生缺页中断次数?  用ps -o majflt,minflt -C program命令查看。  majflt代表major fault,中文名叫大错误,minflt代表minor fault,中文名叫小错误。  这两个数值表示一个进程自启动...
  • C语言进程的内存地址空间分配

    千次阅读 2017-11-30 21:12:04
    图为一个执行进程的内存地址空间。 代码段就是存储程序文本的,所以有时候也叫做文本段,指令指针中的指令就是从这里取得。数据段是存储数据用的,还可以分成初始化为非零的数据区,BSS,和堆(Heap)三个区域。初始...
  • linux c进程内存分配

    2015-10-24 13:15:35
    在任何程序设计环境及语言中,内存管理都十分重要。在目前计算机系统或嵌入式系统中,内存资源仍然是有限。因此在程序设计中,有效地管理...第2节主要介绍C语言中内存分配及释放函数、函数功能,以及如何调用这些
  • Android单个进程内存分配

    千次阅读 2015-12-29 17:28:38
    android不同设备单个进程可用内存是不一样,可以查看/system/build.prop文件。 # This is a high density device with more memory, so larger vm heaps for it. dalvik.vm.heapsize=24m 上面heapsize参数表示...
  • 进程4G虚拟内存空间的分配

    千次阅读 2018-11-13 19:23:39
    当一个程序运行时,系统会为每一个进程分配一个4G的虚拟内存空间,用来保存进程运行所需要的各种资源(详细资源列表后面会谈到),并创建task_struct进程控制块,保存进程的属性(进程ID、父进程、进程状态、使用的...
  • 假如有一个CPU调度的算法,将另一进程换入到刚刚释放的内存空间当中,同时CPU调度器可以将时间片分配给其他已在内存中的进程,当每个进程用完时间片,它将与另一进程进行交换 这样的交换策略的变种其实可以被用在...
  • 一、预备知识——程序的内存分配  一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— —由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、...
  • Android单个进程内存分配策略

    千次阅读 2012-03-10 16:26:47
    android不同设备单个进程可用内存是不一样,可以查看/system/build.prop文件。 # This is a high density device with more memory, so larger vm heaps for it. dalvik.vm.heapsize=24m 上面heapsize参数表示...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,053
精华内容 8,821
关键字:

进程的内存分配