高明网站开发,志愿者网站 建设方案,wordpress还有人在用吗,vs做网站各种控件的使用目录
内存泄漏
内存模型 、进程地址空间
堆和栈的区别
内存对齐
大端小端及判断
虚拟内存有什么作用 内存泄漏
概念:
是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况, 内存泄漏并不是指内存在物理上的消失, 而是应用程序分配了某段内存后, 因为设计错误…目录
内存泄漏
内存模型 、进程地址空间
堆和栈的区别
内存对齐
大端小端及判断
虚拟内存有什么作用 内存泄漏
概念:
是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况, 内存泄漏并不是指内存在物理上的消失, 而是应用程序分配了某段内存后, 因为设计错误, 失去了对该段内存的控制, 因而造成了内存的浪费.
new和malloc申请资源使用后, 没有用delete和free释放子类继承父类时, 父类的析构函数不是虚函数未关闭的文件或资源
危害:
长期运行的程序出现内存泄漏, 影响很大; 出现内存泄漏会导致响应越来越慢, 最终卡死.
避免:
计数法使用new或者malloc时让该数1delete或free时该数-1程序执行完打印这个计数如果不为0则表示存在内存泄露一定要将基类的析构函数声明为虚函数对象数组的释放一定要用delete []有new就有delete有malloc就有free保证它们一定成对出现出问题了使用内存泄漏工具检测。
内存泄漏非常常见解决方案分为两种
事前预防型。如智能指针等。事后查错型。如泄漏检测工具。
Linux下可以使用Valgrind工具Windows下可以使用CRT库 内存模型 、进程地址空间 如上图从低地址到高地址用户空间内存从低到高分别是 6 种不同的内存段
代码段包括二进制可执行代码。只读包含一些只读的变量。数据段包括已初始化的静态常量和全局变量BSS 段包括未初始化的静态变量和全局变量堆段包括动态分配的内存从低地址开始向高地址增长动态申请内存用由new分配的内存块其释放由程序员控制一个new对应一个delete文件映射段包括动态库、共享内存等从低地址开始向上增长最后还有一个共享区位于堆和栈之间。栈段存储局部变量、函数参数值。栈从高地址向低地址增长。是一块连续的空间。在不需要时自动清除的存储区。
Linux下的进程地址空间具体是由mm_struct实现的
struct mm_struct{unsigned int code_start;unsigned int code_end;unsigned int init_start;unsigned int init_end;unsigned int uninit_start;unsigned int uninit_end;unsigned int heap_start;unsigned int heap_end;unsigned int stack_start;unsigned int stack_end;
};
比如堆向上增长栈向下增长实际上就是改变mm_struct中的堆和栈的起始指针来实现的 为什么要有进程地址空间 为了实现多任务操作系统中的多进程隔离和独立运行以确保不同进程之间的互不干扰和安全性。 隔离和保护每个进程都有自己独立的地址空间使得不同进程之间的内存互不干扰。这种隔离性确保了一个进程的错误或恶意行为不会对其他进程造成影响提高了系统的稳定性和安全性。相对地址一致性每个进程都认为自己的地址空间是从0开始的并且认为看到的是相同的地址空间范围。这种相对地址一致性使得进程可以使用相对地址进行内存操作而不必关心其他进程的地址空间。独立内存每个进程都认为自己独占内存可以自由分配和管理自己的内存资源。这使得进程可以在不互相干扰的情况下运行并且不需要担心其他进程的内存使用情况。虚拟内存进程地址空间还支持虚拟内存的概念允许操作系统在物理内存有限的情况下为每个进程提供大于物理内存的虚拟内存空间。这通过将部分数据存储在磁盘上根据需要进行页面调度提高了内存利用率。 堆和栈的区别
分配方式
栈由编译器自动分配和管理程序员无需手动控制栈内存的分配和释放。局部变量、函数参数以及函数调用上下文等都存储在栈上。
堆由程序员手动申请和释放通常使用newC或mallocC等函数分配内存并使用deleteC或freeC来释放内存。堆用于存储动态分配的数据如动态数组、对象实例等。
空间大小限制
栈的大小通常是有限的具体大小由编译器或操作系统设置。栈的大小在编译时或运行时可以进行配置但总是有限的。
堆的大小受限于系统可用的虚拟内存大小通常比栈要大得多。堆大小受限于计算机系统中有效的虚拟内存32bit 系统理论上是4G所以堆的空间比较灵活比较大
栈空间和堆区的大小是由操作系统和编译器决定的不同系统和编译器可能会有不同的默认值。一般来说栈空间默认是1MB或2MB而堆区一般是1GB到4GB之间。
内存管理机制
栈的内存管理由编译器自动完成变量的生命周期与其作用域相对应。栈内存的分配和释放是隐式的不需要程序员干预。
堆的内存管理由程序员手动控制。程序员负责显式地分配堆内存并在不再需要时释放它。如果不正确地管理堆内存可能会导致内存泄漏或悬挂指针等问题。
碎片问题
栈内存通常不会出现碎片问题因为栈的内存分配和释放都是线性的按照函数调用的顺序进行。
堆内存可能会出现碎片问题特别是在频繁进行动态内存分配和释放操作时。这可能导致内存空间的不连续影响程序的性能。
生长方向
栈的生长方向通常是向下的即从高地址向低地址增长。这意味着栈的顶部在分配时逐渐向较低的地址移动。
堆的生长方向通常是向上的即从低地址向高地址增长。堆内存在动态分配时逐渐向较高的地址分配。
分配效率
栈由编译器和操作系统提供的支持分配和释放内存的效率较高通常采用硬件级别的指令进行操作。
堆的内存分配和释放需要程序员显式地调用函数效率相对较低并可能涉及复杂的内存管理机制。
形象的比喻
栈就像我们去饭馆里吃饭只管点菜发出申请、付钱、和吃使用吃饱了就走不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作他的好处是快捷但是自由度小。
堆就象是自己动手做喜欢吃的菜肴比较麻烦但是比较符合自己的口味而且自由度大。 内存对齐
内存对齐涉及到如何存储和访问数据以提高计算机的性能和效率。确保数据按照一定的规则存储在内存中以便于有效地访问。
结构体的对齐规则
第一个成员在与结构体变量偏移量为0的地址处。即结构体的首地址处即对齐到0处其他成员变量要对齐到最小对齐数的整数倍的地址处。结构体的总大小为最大对齐数的整数倍。
对齐数 该结构体成员变量自身的大小与编译器默认的一个对齐数的较小值。
注VS中的默认对齐数为8不是所有编译器都有默认对齐数当编译器没有默认对齐数的时候成员变量的大小就是该成员的对齐数。
要修改编译器的默认对齐数我们需要借助于以下预处理命令#pragma pack(...)
为什么存在内存对齐
平台原因移植原因 不是所有的硬件平台都能访问任意地址上的任意数据的某些平台只能在某些地址处取得某些特定类型的数据否则抛出硬件异常。
比如当一个平台要取一个整型数据时只能在地址为4的倍数的位置取得那么这时就需要内存对齐否则无法访问到该整型数据。
性能原因 数据结构尤其是栈应该尽可能的在自然边界上对齐。原因在于为了访问未对齐内存处理器需要作两次内存访问而对齐的内存访问仅需一次。
其实结构体的内存对齐是拿空间来换取时间的做法。 大端小端及判断
大端模式是指数据的低位(就是权值较小的后面那几位)保存在内存的高地址中而数据的高位保存在内存的低地址中地址由小向大增加而数据从高位往低位放。
小端模式是指数据的高位(就是权值较大的前面那几位)保存在内存的高地址中而数据的低位保存在内存的低地址中地址由大向小增加而数据从低位往高位放 。 大小端的意义在于确保数据在不同的计算机体系结构之间正确传递、解释和处理保证系统之间的互操作性和数据的可移植性。 例如32bit的数字0x12345678
所以在Socket编程中往往需要将操作系统所用的小端存储的IP地址转换为大端存储这样才能进行网络传输
小端模式中的存储方式为 大端模式中的存储方式为 如何在代码中进行判断呢
方式一使用强制类型转换-这种法子不错
#include iostream
using namespace std;
int main()
{int a 0x1234;//由于int和char的长度不同借助int型转换成char型只会留下低地址的部分char c (char)(a);if (c 0x12)cout big endian endl;else if(c 0x34)cout little endian endl;
}
方式二巧用union联合体
#include iostream
using namespace std;
// union联合体的重叠式存储endian联合体占用内存的空间为每个成员字节长度的最大值
union endian
{int a;char ch;
};
int main()
{endian value;value.a 0x1234;//a和ch共用4字节的内存空间if (value.ch 0x12)cout big endianendl;else if (value.ch 0x34)cout little endianendl;
} 虚拟内存有什么作用
虚拟内存可以使得进程对运行内存超过物理内存大小因为程序运行符合局部性原理CPU 访问内存会有很明显的重复访问的倾向性对于那些没有被经常使用到的内存我们可以把它换出到物理内存之外比如硬盘上的 swap 区域。当进程需要访问被置换出去的页时它们会被重新加载到物理内存中。这种机制允许了更大的程序运行而不受物理内存的限制。提高内存利用率虚拟内存系统可以更好地利用物理内存资源。只有进程当前需要的部分内存被加载到物理内存中而不是将整个程序加载到内存中。这减少了内存浪费允许多个进程在有限的物理内存中共存。虚拟内存为每个进程提供了独立的地址空间每个进程有自己的页表这样一个进程无法直接访问其他进程的内存从而提高了安全性和隔离性。即使两个进程使用相同的虚拟地址它们映射到不同的物理内存位置。页表里的页表项中除了物理地址之外还有一些标记属性的比特比如控制一个页的读写权限标记该页是否存在等。在内存访问方面操作系统提供了更好的安全性。