zoukankan      html  css  js  c++  java
  • 程序的内存分布

    这里以 Linux 为例,用 C 语言进行演示。

    内存模型

    - 内存空间名称 内容 读写操作 分配时机
    高地址 kernel 内核空间 命令行参数、环境变量等 不可读写 程序运行时
    - stack 栈空间 局部变量 可读写 程序运行时
    - heap 堆空间 malloc() new() 内存分配函数创建 可读写 程序运行时
    - 全局数据空间(初始化的和未初始化的) 静态变量、全局变量 可读写 编译时
    - 只读数据空间 程序的只读数据(常量) 只读 编译时
    低地址 代码段 程序的机器码,相同程序的多个运行实体之间共享 只读 编译时
    • 任何对代码段的写操作都会导致 segmentation fault 段错误
    • 常量、静态变量、全局变量都是在编译时分配内存空间

    查看可执行文件的结构

    size 查看可执行文件的内存分布

    可以通过 size 命令查看可执行文件的内存分配,其中 text 的大小对应程序的只读空间(代码段和只读数据段),data 对应初始化了的全局数据、静态变量,bss 是未初始化数据段,包含未经初始化的全局变量和静态变量。详细例子可以参考:https://blog.csdn.net/love_gaohz/article/details/50522447,简单示例如下:

    #include <stdio.h>
    
    int b;
    int main()
    {
        int a = 888;
    }
    

    上面代码中有未初始化的全局变量,编译后用 size 查看:

    [root@VM_139_38_centos 20190121]# size build 
       text	   data	    bss	    dec	    hex	filename
       1127	    540	     12	   1679	    68f	build
    

    修改 C 代码,初始化全局变量:

    #include <stdio.h>
    
    int b = 666;
    int main()
    {
        int a = 888;
    }
    

    初始化全局变量后,编译后用 size 查看:

    [root@VM_139_38_centos 20190121]# size build 
       text	   data	    bss	    dec	    hex	filename
       1127	    544	      8	   1679	    68f	build
    

    nm 查看可执行文件的标签

    # nm build 
    000000000060102c D b
    0000000000601030 B __bss_start
    0000000000601030 b completed.6355
    0000000000601028 D __data_start
    0000000000601028 W data_start
    0000000000400430 t deregister_tm_clones
    00000000004004a0 t __do_global_dtors_aux
    0000000000600e18 t __do_global_dtors_aux_fini_array_entry
    0000000000400588 R __dso_handle
    0000000000600e28 d _DYNAMIC
    0000000000601030 D _edata
    0000000000601038 B _end
    0000000000400574 T _fini
    00000000004004c0 t frame_dummy
    0000000000600e10 t __frame_dummy_init_array_entry
    00000000004006b8 r __FRAME_END__
    0000000000601000 d _GLOBAL_OFFSET_TABLE_
                     w __gmon_start__
    00000000004003a8 T _init
    0000000000600e18 t __init_array_end
    0000000000600e10 t __init_array_start
    0000000000400580 R _IO_stdin_used
                     w _ITM_deregisterTMCloneTable
                     w _ITM_registerTMCloneTable
    0000000000600e20 d __JCR_END__
    0000000000600e20 d __JCR_LIST__
                     w _Jv_RegisterClasses
    0000000000400570 T __libc_csu_fini
    0000000000400500 T __libc_csu_init
                     U __libc_start_main@@GLIBC_2.2.5
    00000000004004ed T main
    0000000000400460 t register_tm_clones
    0000000000400400 T _start
    0000000000601030 D __TMC_END__
    
    

    strings 查看可执行文件的常量

    [root@VM_139_38_centos 20190121]# strings build 
    /lib64/ld-linux-x86-64.so.2
    Z%1X
    libc.so.6
    printf
    __libc_start_main
    __gmon_start__
    GLIBC_2.2.5
    UH-8
    UH-8
    []AA]A^A_
    address: 
    const is:%p
     global is %p
     local is %p
     function main is %p
    ;*3$"
    ...
    

    下面的例子演示了各种类型变量常量的内存地址的位置:

    #include <stdio.h>
    
    const int a = 666;
    int b = 777;
    char * str = "hello
    ";
    int main()
    {
    	int c = 888;
    	printf("address: 
    const is:%p
     global is %p
     local is %p
     function main is %p
     string str is %p", &a, &b, &c, main, &str);
    	
    	unsigned char * p = main;
    	//p[0] = 0x0; // 这里访问只读的代码段会报错
    	str[3] = 'z'; // 这里访问只读的代码段会报错
    }
    

    输出为:

    address: 
    const is:0x400600
     global is 0x601034
     local is 0x7ffdb921023c
     function main is 0x40052d
     string str is 0x601040
    

    堆和栈的区别

    只读空间在程序运行之前就分配好了,运行结束后才回收。

    管理方式和分配方式不同

    • 程序的栈由编译器自动管理。程序运行时每个函数的变量放在栈 stack 中,函数返回时函数中的局部变量出栈释放。
    • 程序的堆是动态分配的,由代码控制。可以通过 malloc() 和 free() 函数动态扩展和缩减(C++ 中对应 new() 和 delete()),malloc 可以参考:https://www.cnblogs.com/Commence/p/5785912.html
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	char *p = (char *)malloc(100);
    	if (p == NULL) exit(1);
    	int *a;
    	a = (int *)malloc(sizeof(int));
    	if (a == NULL) {
    		free(p);
    		exit(1);
    	}
    	free(a);
    	printf("end");
    }
    

    碎片水平不同

    • 堆的分配和回收会造成内存空间的不连续,造成大量的碎片,使程序效率降低。
    • 栈不存在碎片问题,因为栈是先进后出的队列,永远不可能有一个内存块从栈中间弹出。
  • 相关阅读:
    数据库开发及ADO.NET
    C#典型案例及分析
    Visual Studio 2017 常用快捷键
    C#方法(函数)
    Linux常用命令----RPM包管理
    Linux常用命令----VIM命令
    Linux常用命令----压缩解压命令
    Linux常用命令----用户管理命令
    文件搜索命令
    Linux常用命令----权限管理命令
  • 原文地址:https://www.cnblogs.com/kika/p/10851507.html
Copyright © 2011-2022 走看看