理解代码的内存消耗,最关键是要知道本身所用编程语言的内存管理。node
不一样语言的内存管理
不一样的编程语言各自的内存管理方式。python
-
Java 依赖JVM来作内存管理,不了解jvm内存管理的机制,极可能会因一些错误的代码写法而致使内存泄漏或内存溢出程序员
-
Python内存管理是由私有堆空间管理的,全部的python对象和数据结构都存储在私有堆空间中。程序员没有访问堆的权限,只有解释器才能操做。github
例如Python万物皆对象,而且将内存操做封装的很好,因此python的基本数据类型所用的内存会要远大于存放纯数据类型所占的内存,例如,咱们都知道存储int型数据须要四个字节,可是使用Python 申请一个对象来存放数据的话,所用空间要远大于四个字节。面试
C++的内存管理
以C++为例来介绍一下编程语言的内存管理。编程
若是咱们写C++的程序,就要知道栈和堆的概念,程序运行时所需的内存空间分为 固定部分,和可变部分,以下:数据结构
固定部分的内存消耗 是不会随着代码运行产生变化的, 可变部分则是会产生变化的jvm
更具体一些,一个由C/C++编译的程序占用的内存分为如下几个部分:编程语言
-
栈区(Stack) :由编译器自动分配释放,存放函数的参数值,局部变量的值等,其操做方式相似于数据结构中的栈。
-
堆区(Heap) :通常由程序员分配释放,若程序员不释放,程序结束时可能由OS收回
-
未初始化数据区(Uninitialized Data):存放未初始化的全局变量和静态变量
-
初始化数据区(Initialized Data):存放已经初始化的全局变量和静态变量
-
程序代码区(Text):存放函数体的二进制代码
代码区和数据区所占空间都是固定的,并且占用的空间很是小,那么看运行时消耗的内存主要看可变部分。
在可变部分中,栈区间的数据在代码块执行结束以后,系统会自动回收,而堆区间数据是须要程序员本身回收,因此也就是形成内存泄漏的发源地。
而Java、Python的话则不须要程序员去考虑内存泄漏的问题,虚拟机都作了这些事情。
如何计算程序占用多大内存
想要算出本身程序会占用多少内存就必定要了解本身定义的数据类型的大小,以下:
注意图中有两个不同的地方,为何64位的指针就占用了8个字节,而32位的指针占用4个字节呢?
1个字节占8个比特,那么4个字节就是32个比特,可存放数据的大小为2^32,也就是4G空间的大小,即:能够寻找4G空间大小的内存地址。
你们如今使用的计算机通常都是64位了,因此编译器也都是64位的。
安装64位的操做系统的计算机内存都已经超过了4G,也就是指针大小若是仍是4个字节的话,就已经不能寻址所有的内存地址,因此64位编译器使用8个字节的指针才能寻找全部的内存地址。
注意2^64是一个很是巨大的数,对于寻找地址来讲已经足够用了。
内存对齐
再介绍一下内存管理中另外一个重要的知识点:内存对齐。
不要觉得只有C/C++才会有内存对齐,只要能够跨平台的编程语言都须要作内存对齐,Java、Python都是同样的。
并且这是面试中面试官很是喜欢问到的问题,就是:为何会有内存对齐?
主要是两个缘由
-
平台缘由:不是全部的硬件平台都能访问任意内存地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,不然抛出硬件异常。为了同一个程序能够在多平台运行,须要内存对齐。
-
硬件缘由:通过内存对齐后,CPU访问内存的速度大大提高。
能够看一下这段C++代码输出的各个数据类型大小是多少?
struct node{ int num; char cha; }st; int main() { int a[100]; char b[100]; cout << sizeof(int) << endl; cout << sizeof(char) << endl; cout << sizeof(a) << endl; cout << sizeof(b) << endl; cout << sizeof(st) << endl; }
看一下和本身想的结果同样么, 咱们来逐一分析一下。
其输出的结果依次为:
4 1 400 100 8
此时会发现,和单纯计算字节数的话是有一些偏差的。
这就是由于内存对齐的缘由。
来看一下内存对齐和非内存对齐这两种状况下产生的效果区别。
CPU 读取内存不是一次读取单个字节,而是一次性读取一整个 CacheLine ,这个 CacheLine 的大小是 64 字节。为了方便演示,咱们假设 CacheLine 的大小是 4 字节,要读取的也是一个 4 字节的 int。
第一种就是内存对齐的状况,如图:
内存对齐
一字节的char占用了四个字节,空了三个字节的内存地址,int数据从地址4开始。
此时,直接将地址4,5,6,7处的四个字节数据读取到便可。
第二种是没有内存对齐的状况如图:
非内存对齐
char型的数据和int型的数据挨在一块儿,该int数据从地址1开始,那么CPU想要读这个数据的话来看看须要几步操做:
-
由于CPU是四个字节四个字节来寻址,首先CPU读取0,1,2,3处的四个字节数据
-
CPU读取4,5,6,7处的四个字节数据
-
合并地址1,2,3,4处四个字节的数据才是本次操做须要的int数据
此时一共须要两次寻址,一次合并的操做。
你们可能会发现内存对齐岂不是浪费的内存资源么?
是这样的,但事实上,相对来讲计算机内存资源通常都是充足的,咱们更但愿的是提升运行速度。
编译器通常都会作内存对齐的优化操做,也就是说当考虑程序真正占用的内存大小的时候,也须要认识到内存对齐的影响。
总结
很多同窗对这方面的知识很欠缺,基本处于盲区,经过这一篇你们能够初步补齐一下这块。
以后也能够有意识的去学习本身所用的编程语言是如何管理内存的,这些也是程序员的内功。