【JVM】java中的栈内存, 堆内存

Page content

上一篇文章简单整理了栈(stack), 堆(heap), 队列(queue)的结构
这一篇继续整理java中的 占内存,堆内存。

Java把内存划分成两种:一种是栈内存,一种是堆内存。
这里需要解释一下, 这里的堆内存是跟数据结构的堆是完全两码事。

一、栈内存

存放基本类型的变量,以及对象的引用值和函数主体,遵循先入后出的原则。

栈内存在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
当在一段代码块定义一个变量时:

Java在栈中为变量分配内存空间,当超过变量的作用在域后,
Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

Java中的代码是在函数体中执行的,每个函数主体都会被放在栈内存中,

举例子有一个main()函数。  
main()函数里调用了save()函数,那么栈低存储的是main()函数其上面是save()函数。  
等save()函数执行后,先销毁save()函数,再销毁main()函数。  

栈的优势是,栈内存与堆内存相比是非常小的,存取速度比堆要快,栈数据可以共享。
但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。


二、堆内存

给我的感觉是所有的引用类型的值是存在堆内存中,地址(指针)是存在栈内存中。

堆内存是区别于栈区、全局数据区和代码区的另一个内存区域。
堆内存在程序运行中,可以动态的调整申请大小

堆内存的分配过程

当接收到程序的内存申请时:
1. 先游遍操作系统中的记录空闲内存地址的链表,寻找第一个复合大小的节点。  
2. 分配给程序该节点的空间,内存空间中的首地址会记录本次分配的大小,为删除的时能正确的获取长度。  
3. 把该节点从操作系统的空闲节点中删除。

找到的堆节点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。
堆内存的大小受限于计算机系统中有效的虚拟内存。所以堆内存获得的空间比较灵活,也比较大。
但是也是因为这些原因容易产生内存碎片,存取速度也比较慢。


三、内存回收

`栈内存`变量基本上用完就回收了。  
`堆内存`中:  
数组和对象等引用类型在没有变量指向它的时候才被视为垃圾。  
在随后的一个`不确定的时间`被垃圾回收器释放掉。  
因为这些原因回收内存之前会一直占用资源, 也不知道这些资源什么会回收释放。

四、其他数据存储

1、常量池:存放基本类型常量和字符串常量
2、静态域(static):存放静态成员(static定义的)编译的时候直接分配空间。要求程序代码中不允许有可变数据结构(比如可变数组)的存在。
3、非RAM存储:硬盘等永久存储空间

五、编译时分配的空间

最后我们再次已编译的角度去理解静态, 栈, 堆的存储方式。
1. 静态存储分配:
编译的时候直接分配空间, 所以会要求程序代码中不允许有可变数据结构(比如可变数组)的存在。
2. 栈存储分配:
是编译的时候时未知的, 但是运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存。
3. 堆存储分配:
是编译的时候时未知的, 运行时模块入口处都无法确定存储要求 数据结构的内存分配, 执行的过程也是可变的。所以存取速度也比较慢。


欢迎大家的意见和交流

email: li_mingxie@163.com