第4部分 库与运行库---(10)内存

2024-05-11 13:48
文章标签 内存 部分 运行库

本文主要是介绍第4部分 库与运行库---(10)内存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

###################
# 10、内存
###################
# 程序的内存布局
在32位操作系统中,内存空间拥有4GB的寻址能力。操作系统会把高地址的空间分配给内核,称为内核空间。
默认情况下,Windows将高地址的2GB空间分配给内核,Linux将高地址的1GB空间分配给内核。剩下的2GB或3GB的内存空间称为用户空间。
在用户空间里,有许多地址区间有特殊的地位,一般来讲,应用程序使用的内存空间里有如下"默认"的区域。
栈:用于维护函数调用的上下文。栈通常在用户空间的最高地址处分配,通常有数兆字节的大小。
堆:用来容纳应用程序动态分配的内存区域,malloc或new分配内存时,得到的内存来自堆里。堆通常存在于栈的下方(低地址方向),可以有几十或数百兆字节。
可执行文件映像:存储着可执行文件在内存里的映像,由装载器在装载时将可执行文件的内存读取或映射到这里。
保留区:对内存中受到保护而禁止访问的内存区域的总称。
栈向低地址增长,堆向高地址增长。当栈或堆现有的大小不够用时,它将按照图中的增长方向扩大自身的尺寸,直到预留的空间被用完为止。

Q:程序出现"段错误(segment fault)" 或者 "非法操作,该内存地址不能read/write" 的错误信息,这是怎么回事?
A:非法指针解引用造成的错误,指针指向一个不允许读或写的内存地址,而程序却试图利用指针来读或写该地址的时候,就会出现这个错误。
最普遍原因有两种:
(1)将指针初始化为NULL,之后没有给它一个合理的值就开始使用指针。
(2)没有初始化栈上的指针,指针的值一般会是随机值,之后就开始使用指针。

# 栈与调用惯例
经典的操作系统里,栈总是向下增长的,栈中的数据是先入后出。
栈顶:由esp的寄存器进行定位,压栈的操作使栈顶的地址减少,弹出的操作使栈顶地址增大。
栈指针:ebp寄存器,ebp寄存器指向了函数活动记录的一个固定位置。

栈保存了一个函数调用所需要的维护信息,常被称为堆栈帧或 活动记录。堆栈帧一般包括如下几方面内容:
(1)函数的返回地址和参数。
(2)临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。
(3)保存的上下文:包括在函数调用前后需要保持不变的寄存器。

在参数之后的数据(包括参数)就是当前函数的活动记录, ebp固定在图中所示的位置,不会随这个函数的执行而变化。
esp始终指向栈顶,随着函数的执行,esp会不断变化。
固定不变的ebp可以用来定位函数活动记录中的各个数据,在ebp之前首先是这个函数的返回地址,它的地址是ebp-4,再往前是压入栈中的参数,它们的地址分别是ebp-8、ebp-12等,视参数数量和大小而定。
ebp所直接指向的数据是调用该函数前ebp的值(Old EBP),这样在函数返回的时候,ebp可以通过读取这个值恢复到调用前的值。

# 堆与内存管理
栈上的数据在函数返回时就会被释放掉,所以无法将数据传递至函数外部。而全局变量没有办法动态地产生,只能在编译的时候定义。
堆是一块巨大 的内存空间,程序可以请求一块连续内存,并自由地使用,这块内存在程序主动放弃之前都会一直保持有效。
第3行用malloc申请了1000个字节的空间之后,程序可以自由地使用这1000个字节,直到程序用free函数释放它。
int main()
{char *p = (char*)malloc(1000);free(p);
}
程序的运行库:管理着堆空间的分配,相当于是向操作系统"批发"了一块较大的堆空间,然后"零售"给程序用。当全部"售完"或程序有大量的内存需求时,再根据实际需求向操作系统"进货"。
Linux下的两种堆空间分配的方式:即两个系统调用,一个是brk()系统调用,另一个是mmap()。
brk()的作用就是设置进程数据段的结束地址,即它可以扩大或者缩小数据段。
brk()的作用实际上就是设置进程数据段的结束地址,即它可以扩大或者缩小数据段(Linux下数据段和BSS合并在一起统称数据段)。
mmap()的作用就是向操作系统申请一段虚拟地址空间,这块虚拟地址空间可以映射到某个文件,当它不将地址空间映射到某个文件时,又称这块空间为匿名空间。
#include <unistd.h>int brk(void *addr);

这篇关于第4部分 库与运行库---(10)内存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/979705

相关文章

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

Java内存区域与内存溢出异常的详细探讨

《Java内存区域与内存溢出异常的详细探讨》:本文主要介绍Java内存区域与内存溢出异常的相关资料,分析异常原因并提供解决策略,如参数调整、代码优化等,帮助开发者排查内存问题,需要的朋友可以参考下... 目录一、引言二、Java 运行时数据区域(一)程序计数器(二)Java 虚拟机栈(三)本地方法栈(四)J

java变量内存中存储的使用方式

《java变量内存中存储的使用方式》:本文主要介绍java变量内存中存储的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、变量的定义3、 变量的类型4、 变量的作用域5、 内存中的存储方式总结1、介绍在 Java 中,变量是用于存储程序中数据

一文详解如何在Python中从字符串中提取部分内容

《一文详解如何在Python中从字符串中提取部分内容》:本文主要介绍如何在Python中从字符串中提取部分内容的相关资料,包括使用正则表达式、Pyparsing库、AST(抽象语法树)、字符串操作... 目录前言解决方案方法一:使用正则表达式方法二:使用 Pyparsing方法三:使用 AST方法四:使用字

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性