Java:堆和栈的区别

33 篇文章 1 订阅


前言

这篇记录笔者复习Java堆栈区别

一、Java堆栈常见问题

在学习Java初的时候,我们便知道Java的对象都是分配在堆中,栈中只保留分配对象的引用;
堆是各线程共享,栈是各线程独有,当笔者使用Java一段时间后,笔者心中产生如下的思考

为什么要分堆和栈?

堆和栈的区别是什么?

为什么堆是线程共享的而栈不是?

我们平时说的堆栈信息、堆栈方法区是什么?

二、问题理解

1.为什么要区分堆和栈

要理解这个问题,我们不妨先理解动态分配静态分配两个概念,这两个概念在C++的学习中,我们经常会碰到和使用

静态分配

一般来说,静态分配用于初始化已知对象大小的时候,比如int a[10];如果我们能够确定这个数组是10个,我们可以使用这种方式。这种方式所需要的内存在编译期间即可确定,因此操作系统便可以预先确定所指定大小内存给变量,并且可以在变量生命周期结束后自动释放。

动态分配

然而在某些场景下,可能需要根据某些情况来申请内存,比如int* a =new int[count];而变量count可能就来自于某个配置文件或者用户手动输入的值。这个时候操作系统就无法再预先确定内存大小,并且不执行到new int[count]这一行代码的时候,是无法知道所要分配的内存大小,因此操作系统分出一块内存,用来进行动态分配。并且规定,动态分配的内存需要由客户端自行管理。

Java中的堆

Java 中的堆

以上是在操作系统中,堆和栈的出现的缘由。由于JVM规范中规定,JVM中的一切对象都存储在堆上(内存逃逸除外)。因此在Java中并不存在对象的静态分配,因此堆和栈的来源看似就非常理所当然。Java中也同样存在编译时便确定的静态变量,包括指向对象的地址指针,其存在于栈中,如此分工,毫无疑问能使一方更加专注于自己所要做的事情,提高效率。

2.堆和栈的区别

  • 堆是运行时确定内存大小,而栈在编译时即可确定内存大小
  • 堆内存由用户管理(Java中由JVM管理),栈内存会被自动释放
  • 栈实现方式采用数据结构中的栈实现,具有(LIFO)的顺序特点,堆为一块一块的内存
  • 栈由于其实现方式,在分配速度上比堆快的多。分配一块栈内存不过是简单的移动一个指针
  • 在JVM中,栈不会被程序员直接使用,程序员操作的一般都是堆。
  • 栈为线程私有而堆为线程共享

区别虽多,但在硬件上实现上,他们本质都是基于RAM

3.为什么堆是线程共享的而栈不是?

这里的堆是线程共享,栈是线程私有仅仅是针对JVM虚拟机,不同的虚拟机可能采用的模式不同

而JVM采用堆是线程共享、栈是线程私有的原因主要如下:
线程间通信有两大机制:
1.共享内存->效率高,但有线程安全问题
2.消息传递->需要拷贝生成消息,线程安全,但是效率慢

  • 堆更注重效率,共享内存便是我们所说的将堆设置为线程间共享的,这样我们能够通过堆中的对象实现数据共享,这样便使得其他线程能够知道某个线程修改了某个数据。但是这样带来的问题可能就有线程安全问题等,但是这样做的优势便在于速度更快和节约内存,Java,C#等使用这种方式
  • 栈更注重线程安全,常常用来存放 函数的参数,函数中使用的自动变量,存放过程活动记录;如果多个线程共享一个Stack会非常的凌乱,不方便管理

4.我们平时说的堆栈信息、堆栈方法区是什么?

堆栈信息

堆栈是一个在计算机科学中经常使用的抽象数据类型。堆栈中的物体具有一个特性: 最后一个放入堆栈中的物体总是被最先拿出来, 这个特性通常称为后进先出(LIFO)队列。

堆栈使用过程如下,内存中开辟一个存储区域,数据一个一个顺序地存入(也就是“压入——push”)这个区域之中。有一个地址指针总指向最后一个压入堆栈的数据所在的数据单元,存放这个地址指针的寄存器就叫做堆栈指示器。开始放入数据的单元叫做“栈底”。数据一个一个地存入,这个过程叫做“压栈”。在压栈的过程中,每有一个数据压入堆栈,就放在和前一个单元相连的后面一个单元中,堆栈指示器中的地址自动加1。读取这些数据时,按照堆栈指示器中的地址读取数据,堆栈指示器中的地址数自动减 1。这个过程叫做“弹出pop”。如此就实现了后进先出的原则。

堆栈溢出的产生是由于过多的函数调用,导致调用堆栈无法容纳这些调用的返回地址,一般在递归中产生。堆栈溢出很可能由无限递归(Infinite recursion)产生,但也可能仅仅是过多的堆栈层级。

堆溢出:不断的new 一个对象,一直创建新的对象,

栈溢出:死循环或者是递归太深,递归的原因,可能太大,也可能没有终止。

我们常说的堆栈信息便是对函数使用数据、函数调用、函数返回地址进行一些记录的信息。

堆栈方法区

堆、栈、堆栈方法区三者共同构成Java中的JVM内存模型
具体如下:

  • 堆区:

提供所有类实例和数组对象存储区域

jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

  • 栈区:

每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中

每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。

  • 方法区:

又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。

方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

运行时常量池都分配在 Java 虚拟机的方法区之中

总结

以上就是笔者总结的Java堆和栈的区别
参考博客
Java堆栈
堆栈溢出的原因

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值