关于 Java 中的堆内存和栈内存

最近在看 Java 书的过程中发现书中多次提到了堆内存和栈内存。在之前学习的数据结构中,我的认知还仅仅限于堆栈,不知道堆和栈是两个不同的概念。问了算法&数据结构大佬( @Simple )之后自己又上网查了查,才发现原来在 Java 中所说的堆和栈是两个不同的内存区,这里做个笔记。

在 Java 中,栈(stack)是由编译器自动分配和释放的一块内存区域,主要用于存放一些基本类型(8种基本类型:int,short, long, byte, float, double, boolean, char)的变量、指令代码、常量以及对象的引用。

堆(heap)是一个程序运行时动态分配的内存区域,在 Java 中,构建对象时所需要的内存从堆中分配。这些对象通过 new 指令来显式建立,堆内存在使用完毕后,由垃圾回收(GC)器来隐式回收的。

区别

1、堆内存是栈内存的一个子集

2、栈内存的存取速度仅次于寄存器,栈内存里面的数据可以共享,但是其中数据的大小和生存期必须在运行前确定。

示例:对于基本类型 int,我们定义

int a = 5;
int b = 5;

前面说过,基本类型的变量都是存在栈内存中,因此这里 int a = 5,实际上就是先在栈内存中新建一个空间,存 a 变量,然后再寻找有没有存放值是 5 的内存地址,这里假设没有,就新建一个内存空间存放“5”这个值,然后将”a”变量指向“5”的地址。接着我们声明 int b = 5,同样的,先在栈中开辟一个变量“b”的内存空间,再查询有没有“5”,由于之前声明“a”的时候已经在栈中创建了“5”,这里就把”b”指向之前存放“5”的内存空间的地址。这样栈中两个变量“a”和“b”都指向了栈中存放“5”的内存空间地址。这就说明栈中的数据是可以共享的。同时,变量 a 和 b 由于都是整型,因此大小已知,变量在声明时也指定了一个生存期(某一个代码块内,超出这个代码块就失效)。

这里还要注意的是,这里 a 和 b 同时指向了值“5”,也可以叫做字面值的引用。这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。比如我们再让 a = 6,此时,编译器会寻找栈中是否存在“6”的内存空间,如果有,就将 a 指向该内存空间地址,如果没有,就新建一个内存空间存放“6”,接着将变量 a 指向新建的内存空间的地址。此时变量 b 还是指向原来”5″的内存空间地址, b 的值并没有改变。

3、对堆内存是运行时可动态分配的数据区,从速度看比栈内存慢,堆内存中的数据不共享,大小和生存期都可以在运行时再确定。

new 关键词是运行时在堆内存里创建对象的唯一方法,每 new 一下,不管内容是否相同,一定会创建一个新的对象,这就是堆内存中的数据不共享。

使用时会出现的问题

栈内存

由于栈内存较小,如果栈内存不慎耗尽,就会产生著名的栈溢出(stack overflow)问题,怎么样,是不是感觉这个词很熟悉?那个全球最受欢迎的技术问答网站就是这个 Stack Overflow ,这个网站里的问答可都是高质量的,不像国内的****那样 :(滑稽)

堆内存

如果堆内存使用不当,会产生内存碎片的问题。当回收堆内存时,可能会导致一些小块的但不连续的内存存在。当用户申请一块较大的堆内存时,虽然可用的小块内存综合足够大,本可以满足申请需求,但是由于他们是不连续的,导致申请失败。这些不连续的小块内存就是所谓的内存碎片。

 

先写到这里,后期还遇到什么关于堆内存和栈内存的问题我再在这里补充。


版权说明:
作品 sunriseydy 采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
文章内容如未说明均为原创,欢迎转载,但请注明原作者(sunriseydy)和原文链接(https://blog.sunriseydy.top/technology/code/java/java-heap-stack/)
部分来自互联网的文章,如有侵权,请联系我,24小时内删除,谢谢

微博 QQ 打赏 点赞 0

评论一下呗亲

电子邮件地址不会被公开。 必填项已用*标注