Java开发中,每当我们在程序中使用new生成一个对象,对象的引用存放在栈里,而对象是存放在堆里的。可以看出栈在Java核心的重要位置。今天我们就继续深入Java核心这个系列,为您介绍Java中的栈、局部变量及其之间的关系。
Java中的栈
每当启用一个线程时,JVM就为他分配一个Java栈,栈是以帧为单位保存当前线程的运行状态。某个线程正在执行的方法称为当前方法,当前方法使用的栈帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。当线程执行一个方法时,它会跟踪当前常量池。
每当线程调用一个Java方法时,JVM就会在该线程对应的栈中压入一个帧,这个帧自然就成了当前帧。当执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等等。
Java栈上的所有数据都是私有的。任何线程都不能访问另一个线程的栈数据。所以我们不用考虑多线程情况下栈数据访问同步的情况。
像方法区和堆一样,Java栈和帧在内存中也不必是连续的,帧可以分布在连续的栈里,也可以分布在堆里
Java栈的组成元素——栈帧
栈帧由三部分组成:局部变量区、操作数栈、帧数据区。局部变量区和操作数栈的大小要视对应的方法而定,他们是按字长计算的。但调用一个方法时,它从类型信息中得到此方法局部变量区和操作数栈大小,并据此分配栈内存,然后压入Java栈。
局部变量区 局部变量区被组织为以一个字长为单位、从0开始计数的数组,类型为short、byte和char的值在存入数组前要被转换成int值,而long和double在数组中占据连续的两项,在访问局部变量中的long或double时,只需取出连续两项的第一项的索引值即可,如某个long值在局部变量区中占据的索引时3、4项,取值时,指令只需取索引为3的long值即可。
下面就看个例子,好让大家对局部变量区有更深刻的认识。这个图来自《深入JVM》:
以下是引用片段: public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) { return 0; } public int runInstanceMethod(char c,double d,short s,boolean b) { return 0; } |
上面代码片的方法参数和局部变量在局部变量区中的存储结构如下图:
上面这个图没什么好说的,大家看看就会懂。但是,在这个图里,有一点需要注意:
runInstanceMethod的局部变量区第一项是个reference(引用),它指定的就是对象本身的引用,也就是我们常用的this,但是在runClassMethod方法中,没这个引用,那是因为runClassMethod是个静态方法。
操作数栈和局部变量区一样,操作数栈也被组织成一个以字长为单位的数组。但和前者不同的是,它不是通过索引来访问的,而是通过入栈和出栈来访问的。可把操作数栈理解为存储计算时,临时数据的存储区域。下面我们通过一段简短的程序片段外加一幅图片来了解下操作数栈的作用。
以下是引用片段: int a = 100; int b = 98; int c = a+b; |
从图中可以得出:操作数栈其实就是个临时数据存储区域,它是通过入栈和出栈来进行操作的。
帧数据区除了局部变量区和操作数栈外,Java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些数据都保存在Java栈帧的帧数据区中。
当JVM执行到需要常量池数据的指令时,它都会通过帧数据区中指向常量池的指针来访问它。
除了处理常量池解析外,帧里的数据还要处理Java方法的正常结束和异常终止。如果是通过return正常结束,则当前栈帧从Java栈中弹出,恢复发起调用的方法的栈。如果方法又返回值,JVM会把返回值压入到发起调用方法的操作数栈。
为了处理Java方法中的异常情况,帧数据区还必须保存一个对此方法异常引用表的引用。当异常抛出时,JVM给catch块中的代码。如果没发现,方法立即终止,然后JVM用帧区数据的信息恢复发起调用的方法的帧。然后再发起调用方法的上下文重新抛出同样的异常。
栈的整个结构
在前面就描述过:栈是由栈帧组成,每当线程调用一个Java方法时,JVM就会在该线程对应的栈中压入一个帧,而帧是由局部变量区、操作数栈和帧数据区组成。那在一个代码块中,栈到底是什么形式呢?下面是我从《深入JVM》中摘抄的一个例子,大家可以看看:
代码片段:
执行过程中的三个快照:
上面所给的图,只想说明两件事情,我们也可用此来理解Java中的栈:
1、只有在调用一个方法时,才为当前栈分配一个帧,然后将该帧压入栈。
2、帧中存储了对应方法的局部数据,方法执行完,对应的帧则从栈中弹出,并把返回结果存储在调用方法的帧的操作数栈中。
分享到:
相关推荐
理解操作数栈和局部变量表,有助于理解方法调用和异常处理。 4. **JVM优化**:探讨如何通过调整JVM参数来优化应用程序,如内存配置、线程池大小、GC策略等。同时,书中还会介绍性能分析工具,如JVisualVM、...
堆内存用于存储对象实例,栈内存则存储线程局部变量,方法区存储类信息,本地方法栈服务于JNI调用,程序计数器记录当前线程执行的指令地址。了解这些内存区域的分配和管理,有助于识别和解决内存溢出、内存泄漏等...
书中的实践项目会涉及解析和理解这些指令,如`aload_0`(加载局部变量表的第一个引用)、`invokevirtual`(调用虚方法)等。 2. **类加载机制**:JVM如何找到并加载类文件,包括类加载器、双亲委派模型和类加载过程...
栈和局部变量是JVM中的核心机理,它们是Java虚拟机的重要部分。 JVM是Java虚拟机的核心机理,它是Java语言的运行环境。JVM中的类型的生命周期、方法区、常量池、类加载器、垃圾收集器、栈和局部变量等都是JVM的重要...
堆内存用于存储对象实例,而栈内存则存储线程私有的局部变量、方法参数和运算结果。方法区存储类的信息,如常量池、字段和方法数据。本地方法栈服务于JNI(Java Native Interface)调用的本地方法。 3. **垃圾收集...
虚拟机栈则为每个线程分配一个独立的栈,存储局部变量、操作数栈、动态链接和方法出口等信息。如果栈深度过大或对象生命周期过长,可能导致栈溢出或内存溢出错误。 在性能优化时,我们关注JVM参数设置,如-Xms和-...
堆是存储对象实例的主要区域,栈则用于存储方法调用时的局部变量,方法区保存类信息,程序计数器记录下一条要执行的指令,本地方法栈服务于JNI(Java Native Interface)调用。 2. **垃圾回收**:Java的自动内存...
每个区域都有其特定的作用,例如方法区存储类信息,堆是对象实例的存储地,栈则用于存储方法调用过程中的局部变量。 4. **垃圾收集**:JVM提供自动内存管理,通过垃圾收集机制回收不再使用的对象所占用的内存空间。...
3. **虚拟机栈**:每个线程都有自己的虚拟机栈,用于存储局部变量、操作数栈、动态链接和方法出口信息。 4. **本地方法栈**:为JNI方法服务,存储本地方法的局部变量。 5. **程序计数器**:记录当前线程执行的字节码...
本文将深入探讨Java中堆内存与栈内存的分配机制,并通过对比分析它们之间的差异,帮助读者更好地掌握Java内存管理的核心概念。 #### 二、堆内存与栈内存概述 ##### 1. 堆内存 堆内存是Java虚拟机(JVM)用于存储...
《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》是一本深受Java开发者喜爱的经典著作,它详细解析了Java虚拟机的工作原理和优化策略。源代码"jvm-demo-code-master"是该书配套的示例代码,包含了许多关于...
总结来说,Java虚拟机栈是Java方法执行的核心,它通过栈帧管理每个方法的执行状态,包括局部变量、操作和返回地址。栈的特性使得它在处理递归调用和快速局部变量访问时非常高效。同时,了解其工作原理有助于优化程序...
栈则是每个线程私有的,用于存储局部变量、方法参数和运算结果。方法区则保存类信息、常量池等内容,这部分在Java 8之后被元空间(Metaspace)所取代。 类加载机制是JVM的重要特性,它包括加载、验证、准备、解析和...
开发者需要实现调用栈,处理方法的调用和返回,包括参数传递、局部变量管理和多态性。 7. 异常处理:JVM的异常处理机制涉及到异常表,用于捕获和处理运行时错误。zvm-jvm需要实现这个功能,确保程序能够正确处理...
Java虚拟机(JVM)是Java平台的核心,它负责执行字节码,提供了内存管理、类加载、垃圾回收等功能,确保了Java的“一次编写,到处运行”的特性。JVM通过解析.class文件中的字节码,将其转换为机器码,从而在本地操作...
《深入Java虚拟机》是Java开发者必读的经典之作,它详细阐述了Java虚拟机(JVM)的工作原理和内部机制,对于理解Java程序的运行方式、优化代码性能以及排查问题具有极其重要的价值。这本书的第二版更是加入了更多...
Java虚拟机(JVM)是Java程序运行的核心组件,...综上所述,JVM指令手册是Java开发者的重要参考资料,它详细列出了所有操作栈和局部变量的指令,有助于我们深入学习JVM的工作原理,进而优化Java代码并解决运行时问题。
Java虚拟机栈是线程私有的,用于存储方法执行的局部变量表、操作数栈、动态链接等信息。当线程请求的栈深度超过JVM允许的深度时,会抛出`StackOverflowError`异常;当JVM无法再扩展栈时,会抛出`OutOfMemoryError`...
3. 栈内存:保存方法调用的状态,包括局部变量、操作数栈和动态链接。 三、垃圾回收 1. 引用计数:简单但无法处理循环引用。 2. 标记-清除:标记无用对象并进行清除,可能导致内存碎片。 3. 复制算法:将存活对象...