这里以 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
[]A\A]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\n";
int main()
{
int c = 888;
printf("address: \nconst is:%p\n global is %p\n local is %p\n function main is %p\n 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");
}

碎片水平不同

  • 堆的分配和回收会造成内存空间的不连续,造成大量的碎片,使程序效率降低。
  • 栈不存在碎片问题,因为栈是先进后出的队列,永远不可能有一个内存块从栈中间弹出。

程序的内存分布 - 以 Linux 为例,基于 C 语言分析的更多相关文章

  1. C++程序的内存分布

    4.文字常量区: p与p1的指针地址一致,且字符串常量是不能被改变的. 5.程序代码区:存放一系列代码. 动态内存 1.按需分配,根据需要分配内存,不浪费. 内存拷贝函数 void *memcpy(v ...

  2. [GeekBand] C++ 内存分布—— new和delete重载的实现及分析

    本文参考文献:GeekBand课堂内容,授课老师:侯捷 :深度探索C++对象模型(侯捷译) :网络资料: http://www.leavesite.com/geekband-cpp-5.html ht ...

  3. Linux 上 C 程序的内存布局

    在仔细研究这个问题之前,我认为 C 程序在内存中只有代码段,堆和栈三部分构成.前几天面试被问到了这个问题,才发现自己的印象是不完全的. 在本文中通过解析析一个 C 程序中变量和函数的地址来分析 C 程 ...

  4. C语言中内存分布及程序运行中(BSS段、数据段、代码段、堆栈)

      BSS段:(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbol的简称.BSS段属于静态内存分配. 数据段 : ...

  5. 应用 Valgrind 发现 Linux 程序的内存问题

    如何定位应用程序开发中的内存问题,一直是 inux 应用程序开发中的瓶颈所在.有一款非常优秀的 linux 下开源的内存问题检测工具:valgrind,能够极大的帮助你解决上述问题.掌握 valgri ...

  6. 应用 Valgrind 发现 Linux 程序的内存问题(转)

    Valgrind 概述 体系结构 Valgrind 是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合.Valgrind由内核(core)以及基于内核的其他调试工具组成.内核类似于一个 ...

  7. [转载]应用 Valgrind 发现 Linux 程序的内存问题

    应用 Valgrind 发现 Linux 程序的内存问题 如何定位应用程序开发中的内存问题,一直是 inux 应用程序开发中的瓶颈所在.有一款非常优秀的 linux 下开源的内存问题检测工具:valg ...

  8. 应用 Valgrind 发现 Linux 程序的内存问题及交叉编译for arm

    Valgrind 概述 体系结构 Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合.Valgrind由内核(core)以及基于内核的其他调试工具组成.内核类似于一个框 ...

  9. STM32程序内存分布

    参考文献:https://www.rt-thread.org/document/site/programming-manual/basic/basic/ 一般 MCU 包含的存储空间有:片内 Flas ...

随机推荐

  1. 单调队列优化DP || [SCOI2010]股票交易 || BZOJ 1855 || Luogu P2569

    题面:P2569 [SCOI2010]股票交易 题解: F[i][j]表示前i天,目前手中有j股的最大收入Case 1:第i天是第一次购买股票F[i][j]=-j*AP[i]; (1<=j< ...

  2. mac下phpize编译提示Cannot find autoconf解决办法

    mac下phpize编译如下报错: /usr/bin/phpizeConfiguring for:PHP Api Version: 20121113Zend Module Api No: 201212 ...

  3. 如何使用 vue-cli 3 的 preset 打造基于 git repo 的前端项目模板

    vue-cli 之 Preset vue-cli 插件开发指南 TLDR 背景介绍 vue-cli 3 完全推翻了 vue-cli 2 的整体架构设计,所以当你需要给组里定制一份基于 vue-cli ...

  4. python接口自动化七(重定向-禁止重定向Location)

    前言 某屌丝男A鼓起勇气向女神B打电话表白,女神B是个心机婊觉得屌丝男A是好人,不想直接拒绝于是设置呼叫转移给闺蜜C了,最终屌丝男A和女神闺蜜C表白成功了,这种场景其实就是重定向了. 一.重定向 1. ...

  5. 【leetcode】1214.Two Sum BSTs

    题目如下: Given two binary search trees, return True if and only if there is a node in the first tree an ...

  6. Git的使用及安装

    1安装. 步骤一 如果是32位就安装32位,64位就安装64,任选一款. 步骤二 步骤三 步骤四 步骤五 步骤六 步骤七 步骤八 步骤九 步骤十 步骤十一 上面的安装完成以后,下面的程序包按要求安装就 ...

  7. 导出csv文件(使用a标签)

    https://blog.csdn.net/oscar999/article/details/16342699   productsCSV(e) { const { download } = this ...

  8. 【知识】location.search获取中文时候会被编码成一串字符

    [转码] 例如:case.html?id='这个是页面的标题' 当想要使用location.search获取?id='这个是页面的标题'的时候,包含的中文会被编码成一串字符串. 所以我们需要进行解码, ...

  9. PHP入门培训教程 PHP变量及常量

         一.PHP5.4的基本语法格式 1.PHP的分割符 $php=true; //分号结束语句 if($php){ echo "真"; //分号结束语句 } //大括号结束语 ...

  10. LCA模板 ( 最近公共祖先 )

    LCA 有几种经典的求取方法.这里只给出模板,至于原理我完全不懂. 1.RMQ转LCA.复杂度O(n+nlog2n+m) 大致就是 DFS求出欧拉序 => 对欧拉序做ST表 => LCA( ...