一、为什么要研究内存管理
程序就是数据加算法
- 写程序是为了解决某个问题,生活中的问题最终被计算机抽象为控制或运算
- CPU中的主要构件就是运算器和控制器,本质上是一堆组合逻辑电路,表现为机器指令集
- 一个问题对应一个程序,一个程序分为多个函数,一个函数分为多个机器指令
- 存储机器指令需要内存,机器指令的执行过程需要内存参与,这是内存的2大作用
- 算法对应机器指令(ROM内存),数据对应RAM内存,CPU对应工作机器
- 越是偏底层的语言,越对内存管理具体化,效率也越高,同时对编程者要求也越高
计算机中如何管理内存
- C++项目大多数对应在操作系统中运行,很少有裸机的
- OS提供最基本的内存管理体系,OS直接管理物理内存,并向应用层提供一套内存接口
- C++语言对OS的内存接口进行封装,提供给编程者一套内存使用方法
- 编程人员写的代码在编译工具链、运行时、OS等体系的帮助下最终在计算机物理层运行
总结
- 内存管理的原理虽然庞大而复杂,然而程序员只需要掌握好C++语言的内存管理语言特性即可
- C++程序容易出bug,主要就是因为内存管理部分的复杂性
- Java、Python等语言提供了更多的封装,所以降低了程序员操作难度和犯错可能性
·
·
二、C++可用内存区域
C语言可用内存区域
- 栈,对应全局变量
- 全局数据区/静态数据区,对应全局变量,静态局部变量
- const数据区,在内存层面是不存在的,是C编译器营造出来的
- 代码段,放可执行程序的,性质是ROM
- 堆,由malloc和free来管理的一块内存
C++新增内存区域
- 自由存储区,由 new 申请得到的动态内存区域
- 代码段只会读不会写,一般不会出任何问题
- 全局数据区和栈区都是自动管理的,只要可用内存足够不会出问题
- const数据区实际上都是由编译器来保证只读的,本质就是普通的内存区域
- 灵活性和风险都集中在堆区,常见问题如内存泄漏、内存碎片等
·
·
三、new和malloc的区别
简单区别
- malloc是C库函数,new是C++运算符关键字
- malloc申请空间大小靠传参确定,而new不需要传参,对象本身大小由编译器自动计算给出
- malloc返回值为(void*) , 因此需要强转,而new返回值类型为确定的对象指针类型
- malloc对应free释放,new对应delete和delete[]释放
深度区别
- malloc只能申请内存不能带初始化,而new可以带初始化
- new会执行类的构造函数,而malloc不会
- malloc失败返回NULL,而new失败引发bad_alloc异常
- 申请和释放数组类型时不同
- Linux平台中new内部是通过malloc实现的,new比malloc多一个调用构造函数
- malloc只是返回一块荒地给你,而new会给你修好路盖好房子规划好田地等
·
·
四、智能指针的引入
指针的优势和劣势
- 指针的本质是一个变量,变量的值是其他对象的地址,因此可以解引用
- 指针本质上对应CPU指令中的间接寻址,所以指针是天然存在的,是CPU设计决定了的
- 指针的优势就是灵活、代码效率高
- 指针的劣势是太灵活,尤其结合动态内存和构造、析构后,在复杂业务中容易出错
如何解决
- 底层不用指针是不可能的,因此这个问题是绕不开的
- 解决方案1:由程序员来自主把控,C/C++典型编程就是这样
- 解决方案2:由程序员和专门设计的自动管理机制共同把控,典型代表就是智能指针
- 解决方案3:由自动管理机制全权把控,程序员不用管,典型代表是Java的垃圾回收机制
智能指针如何实现
- 将普通的简单纯指针封装为栈式复合指针对象,即智能指针对象
- 智能指针本身定义为局部变量,分配在栈内存上,因此本身是自动回收的
- 智能指针内部设计为当智能指针本身要被弹栈释放时,执行事先挂接好的清理函数
- 智能指针的正常使用通过一些提供的方法和运算符重载来使用
智能指针总结
- 智能指针是普通指针的升级版,封装版,本身具备指针的功能,且多出一些自动释放资源机制
- 智能指针进行动态内存管理,要比普通指针多出很多(内存和性能上的)开销
- 智能指针的实现不是唯一的,C++有很多智能指针,各有优劣和使用场景。如:std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost::intrusive_ptr。
- 智能指针也要按照设计去正确使用,否则也会出问题。
·
·
五、Java的垃圾回收机制
Java语言整体框架
- CPU -> 操作系统内核 -> 应用层框架 -> JVM(Java虚拟机)-> Java字节码 -> Java源代码
- Java是解释型语言,而非编译型语言
- Java虚拟机是Java语言的运行时环境,也是Java语言跨平台的关键
Java的垃圾回收机制介绍
- 什么是垃圾?待回收的内存资源,本质是生命周期结束了的变量对象等
- 谁来回收?GC线程,Java虚拟机中的守护线程
- 什么时候回收?GC机制和算法来决定,程序员不用管
- 如何确定谁是垃圾?引用计数法和可达性分析法。介绍见:https://www.cnblogs.com/jiangtunan/p/11025521.html
垃圾回收机制点评
- 不止Java,其他语言如C#也是类似设计理念,典型特征就是语言没有指针的概念
- 垃圾回收机制让程序员免于考虑对象的生命周期和资源的申请与释放,编程难度大减
- 垃圾回收机制的稳定性和效率取决于运行时环境(JVM等)设计和实现的好坏
- 垃圾回收机制用效率和内存资源成本,换来了更简单不易错的语言特性