十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java堆栈叫法专坑新人!堆和栈的区别,一个存储对象一个记方法调用,整不明白就别写代码了

Java堆栈叫法专坑新人!堆和栈的区别,一个存储对象一个记方法调用,整不明白就别写代码了 有点明白堆是heap, 栈是stack, 而堆栈实际上是栈。嗯呐, 我是真的特别不喜欢“堆栈”这种称谓, 它很容易致使新人掉进坑里。JVM的规范使得每个Java线程享有自身独立的JVM栈, 这就是Java方法的调用栈。与此同时, JVM规范为了能够让代码去调用Java代码, 并且还能让Java代码调用方法, 于是规定每个Java线程都有自己独立的方法栈。这俩均是JVM规范所规定的概念范畴内的事物, 并非是讲具体的JVM实现切实要为每个Java线程开辟两个独立的栈。拿JDK / 的VM来说, 它运用所谓的“mixed stack”, 即于同一个调用栈里放置Java方法的栈帧以及方法的栈帧, 故而每个Java线程实际上仅有一个调用栈, 此调用栈融合了JVM规范的JVM栈与方法栈这两个概念。JVM里叫做“堆”heap的部分专门用来存放Java对象的内存区域。所以依据这个定义, Java对象全都处于堆上。需留意, 此“堆”并非数据结构层面的那个堆Heap (data ), 一种有序的树, 而是动态内存分配意义范畴的堆——用以管理动态生存期的内存区域。JVM的堆被同一个JVM实例里的所有Java线程共同分享。它通常由某种自动内存管理机制进行管理 , 这种机制通常称作“垃圾回收” , GC。JVM规范并不硬性规定JVM实现采用何种GC算法。把堆同栈区分开来, 依据其对应功能, 栈内存肩负起存储局部变量以及方法调用的职责, 不过堆内存充当起存储Java里对象的场所, 各类变量, 像是成员变量, 局部变量, 或者类变量, 它们所指向的对象统统存储于堆内存区域内。以共享性为着眼点, 栈内存具备线程私有的特性, 堆内存则是所有线程都可共有的。当涉及异常错误方面, 一旦栈内存或者堆内存出现不足的状况, 就会抛出异常, 具体而言, 栈空间不足时会出现java.lang., 堆空间不足时会出现java.lang.。就空间大小而言, 栈的空间大小相较于堆的要小很多。深入理解栈, 栈帧由三部分构成, 分别是局部变量区、操作数栈、帧数据区, 局部变量区和操作数栈大小依对应的方法而定, 它们按字长计算, 调用一个方法时, 它从类型信息里获取此方法局部变量区和操作数栈大小, 依据此来分配栈内存, 随后压入Java栈。局部变量区, 它被组织成以一个字长作为单位, 且从0开始计数的数组, 其中类型为short、byte以及char的值, 如果要存入数组的话, 在存入前提要被转换成int值并且long和在这部数组中占据连续的两项, 如果说在访问局部变量中的long或的时候, 仅仅只需取出连续两项的第一项的索引值就行啦, 就好像某个long值在局部变量区中等所在的索引是这三项, 第一项是比如说是3, 它的第二项是index对应的4那样, 取值时刻, 指令仅仅只需取索引为3的long值就可以的。像是这样的代码以及图所展示的: int (int i,long l,float f, d, o,byte b) { 0;} int (char c, d,short s, b) { 0;}!()### 操作数栈, 它跟局部变量区是一样的情况, 操作数栈同样被构建成一个以字长作为单位的数组, 然而和前者存在差异的是, 它并非借助索引来进行访问, 而是依靠入栈以及出栈这种方式来实现访问的, 能够将操作数栈理解成在存储计算的时候, 用于临时数据的一个存储区域。紧接着, 借助一段简短的程序片段, 再搭配一幅图片, 以此来弄明白操作数栈所具备的作用。 int a 100; int b 98; int c ab; !()由图能够推断出: 操作数栈实际上就是一个临时的数据存储区域, 它依靠入栈以及出栈的方式来开展操作。### 帧数据区 在局部变量区以及操作数栈之外, java栈帧还需要一些数据去支撑常量池解析、正常方法返回以及异常派发机制。那些数据全都被保存在java栈帧的帧数据区之内。当JVM执行至需要常量池数据的指令之际, 它都会借助帧数据区中指向常量池的指针去访问它。 除了对常量池解析进行处理之外, 帧里的数据还要处理java方法的正常结束以及异常终止。要是通过正常结束, 那么当前栈帧会从Java栈中弹出, 恢复发起调用的方法的栈。要是方法存在返回值, JVM会将返回值压入到发起调用方法的操作数栈。帧数据区得保存一个针对此方法异常引用表的引用, 这是为了去处理java方法里的异常状况。当异常被抛出时, JVM会把代码给予catch块中的代码。要是没找到, 方法会马上去终止, 接着JVM会利用帧区数据的信息, 去恢复发起调用的方法的帧。之后在发起调用方法的上下文里, 再次抛出同样的异常。### 栈的整个结构, 栈是由栈帧构成的, 每当线程调用一个java方法之际, JVM会在该线程对应的栈当中压入一个帧, 然而帧是由局部变量区、操作数栈以及帧数据区组成的。那在一个代码块中栈到底是什么形式呢下面是《深入JVM》里的一个例子: class Main{void (){(1,88.88);.out.();}(int i, d){i d;}}执行进程里的三个快照: !()上面这幅图表明两件事儿: 1、唯有在调用某一个方法之际, 才会为当前的栈分配一个帧, 接着把这个帧压进栈2、帧当中存放着对应方法的局部数据, 当方法执行完毕后, 对应的帧便从栈当中弹出, 并且将返回结果存储在调用该方法的帧的操作数栈当中。按照操作系统里进程的内存结构来阐释, 下图描绘的是linux里一个进程的虚拟内存分布情况, 图中0号地址处于最下方, 越往上方内存地址便越大, 以32位地址的操作系统而言, 一个进程所能拥有的虚拟内存地址范围是0到2的32次方, 可划分为两部分, 一部分被用于其他用途, 剩余的则供进程自身使用, 也就是图中的部分, 普通Java程序所使用的正是这部分。
返回列表