`
躁动的绵羊
  • 浏览: 95920 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

深入JVM——栈和局部变量

阅读更多

     java栈概述

 

       记得当初我学习java时,常常听见身边的朋友说:“你要记住,当new一个对象时,对象的引用存放在栈里,而对象是存放在堆里的。当时,听到这句教导时,脑海里立即出现栈的模型——里面存的仅仅是个引用。最近,看了下《深入JVM,才发现,原来栈并不是我想象的那么简单,它和我想象中的那个栈的结构差别非常大。

     每当启用一个线程时,JVM就为他分配一个JAVA栈,栈是以帧为单位保存当前线程的运行状态。某个线程正在执行的方法称为当前方法,当前方法使用的栈帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。当线程执行一个方法时,它会跟踪当前常量池。

     每当线程调用一个java方法时,JVM就会在该线程对应的栈中压入一个帧,这个帧自然就成了当前帧。当执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等等。

java栈上的所有数据都是私有的。任何线程都不能访问另一个线程的栈数据。所以我们不用考虑多线程情况下栈数据访问同步的情况。

像方法区和堆一样,java栈和帧在内存中也不必是连续的,帧可以分布在连续的栈里,也可以分布在堆里

 

java栈的组成元素——栈帧

 

栈帧由三部分组成:局部变量区、操作数栈、帧数据区。局部变量区和操作数栈的大小要视对应的方法而定,他们是按字长计算的。但调用一个方法时,它从类型信息中得到此方法局部变量区和操作数栈大小,并据此分配栈内存,然后压入Java栈。

 

局部变量区 局部变量区被组织为以一个字长为单位、从0开始计数的数组,类型为shortbytechar的值在存入数组前要被转换成int值,而longdouble在数组中占据连续的两项,在访问局部变量中的longdouble时,只需取出连续两项的第一项的索引值即可,如某个long值在局部变量区中占据的索引时34项,取值时,指令只需取索引为3long值即可。

     说再多也没用,下面就看个例子,好让大家对局部变量区有更深刻的认识。这个图来着《深入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方法中的异常情况,帧数据区还必须保存一个对此方法异常引用表的引用。当异常抛出时,JVMcatch块中的代码。如果没发现,方法立即终止,然后JVM用帧区数据的信息恢复发起调用的方法的帧。然后再发起调用方法的上下文重新抛出同样的异常

 

   

   栈的整个结构

  

  在前面就描述过:栈是由栈帧组成,每当线程调用一个java方法时,JVM就会在该线程对应的栈中压入一个帧,而帧是由局部变量区、操作数栈和帧数据区组成。那在一个代码块中,栈到底是什么形式呢?下面是我从《深入JVM》中摘抄的一个例子,大家可以看看:

 

   代码片段:

  

  

     执行过程中的三个快照:

   

 

  上面所给的图,只想说明两件事情:

   1.  只有在调用一个方法时,才为当前栈分配一个帧,然后将该帧压入栈

   2.  帧中存储了对应方法的局部数据,方法执行完,对应的帧则从栈中弹出,并把返回结果存储在调用 方法的帧的操作数栈中

 

   

 

 

  

  • 大小: 18 KB
  • 大小: 89.7 KB
  • 大小: 11 KB
  • 大小: 121 KB
22
1
分享到:
评论
6 楼 newvirus 2010-10-11  
很好的文章 值得学习
5 楼 grantsw 2010-10-05  
博主的资料能否共享下,
4 楼 bugmenot 2010-10-04  
引用

注释好像不对,long是占连续的两个字,word,不是两个字节,byte。JVM把自己的字长定义为32位或者说4个字节,不受实际机器的字长影响
3 楼 iq527 2010-10-03  
endall 写道
栈的整个结构 图最右边的注释有问题,应该是addTwoTypes的操作结果存放在addAndPrint的操作数据栈中。
addTwoTypes,addAndPrint栈桢在栈中的上下关系似乎也有点问题。addTwoTypes应该在栈顶,在addAndPrint的上面。

  至于这个栈的生长方向,好像要取决于虚拟机具体的实现的吧?
2 楼 zzc_zj 2010-10-02  
分析得很详细,学习了
1 楼 endall 2010-10-01  
栈的整个结构 图最右边的注释有问题,应该是addTwoTypes的操作结果存放在addAndPrint的操作数据栈中。
addTwoTypes,addAndPrint栈桢在栈中的上下关系似乎也有点问题。addTwoTypes应该在栈顶,在addAndPrint的上面。

相关推荐

    深入JVM内核—原理、诊断与优化视频教程-4.GC算法与种类

    本教程——“深入JVM内核—原理、诊断与优化视频教程”着重讲解了JVM的内部机制,特别是关于垃圾收集(Garbage Collection, GC)的算法和种类,这对于理解和提升Java应用性能至关重要。 一、JVM内存模型 首先,...

    jvmjava,java实现的JVM。.zip

    《Java实现的JVM——深入理解与实践》 在计算机科学领域,Java虚拟机(JVM)是Java语言的关键组成部分,它使得Java程序能够在不同平台上运行而无需重新编译。本项目“jvmjava”是一个开源项目,由Java语言实现,...

    深入java虚拟机(一)——java虚拟机底层结构详解1

    3. **执行环境(Execution Environment)**:包含了方法的上下文信息,如上次调用的方法、局部变量指针和操作数栈的状态。 然后,**JVM碎片回收堆(Garbage Collection Heap)**是JVM内存管理的重要部分,用于动态...

    Java JVM Instruction Set

    当从`main()`函数中调用`Min()`函数时,JVM会创建一个新的栈帧来保存局部变量和参数,并跳转到相应的字节码位置继续执行。 ##### 访问Min()函数中的参数 由于JVM采用栈式架构,访问参数通常涉及从栈顶弹出值并加载...

    30+个视频+深入理解Java虚拟机(jvm优化+内存模型+虚拟机原理)

    每个线程都有一个私有的栈空间,用于存储局部变量、操作数栈、动态链接等信息。栈中的内存是线程私有的,并且随着方法调用和退出而动态地扩展和收缩。 ### JVM工作原理 #### 类加载机制 JVM通过类加载器将类加载到...

    mini-jvm使用 Java 8 实现 jvm

    本文将深入探讨一个特别的项目——“mini-jvm”,这是一个使用Java 8语言实现的简化版JVM,旨在帮助开发者更好地理解JVM的工作原理。 一、Java 8基础 在深入mini-jvm之前,我们需要了解Java 8的一些关键特性。Java...

    JVM性能调优-JVM内存整理及GC回收.pdf_java_jvm_

    3. **栈内存(Stack Memory)**:每个线程都有自己的程序计数器、虚拟机栈和本地方法栈,用于存储局部变量、方法参数、运算结果等。 4. **PC寄存器(Program Counter Register)**:保存当前线程正在执行的字节码...

    MiniJavaVM——一个Java虚拟机的设计和实现

    本地变量表存储了方法的局部变量和参数。 接着,文章会详细阐述MiniJavaVM的字节码解释器实现。解释器负责读取字节码指令,执行对应的运算,并更新操作数栈和本地变量表。解释器的优化通常包括指令流水线、动态编译...

    JVM笔记(阳哥).zip

    此外,JVM还有一块特殊的内存区域——方法区,用于存储类的信息,如类的静态变量和常量池等。理解这些内存区域的划分和工作方式,对于优化内存使用和避免内存泄漏至关重要。 三、类加载机制 JVM的类加载机制包括...

    jvm内存管理,pdf

    2. **Java虚拟机栈**:用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每个线程都有一个独立的栈。 3. **本地方法栈**:类似于Java虚拟机栈,但用于支持本地方法调用。 4. **Java堆**:所有线程共享的...

    java基础之JVM

    每个线程创建时都会为其创建一个独立的栈,用于存储局部变量、操作数栈、动态链接、方法出口等信息。线程私有的,生命周期与线程相同。栈的特点是快速申请与释放内存,但空间相对较小。 以上概述了JVM的基本概念...

    从 0 开始带你成为JVM实战高手.txt

    - **栈内存**:线程私有的,主要用于存储局部变量、操作数栈、动态链接等信息。 - **方法区**:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码缓存等数据。 2. **垃圾回收机制**:JVM通过自动...

    JVM脑图.zip

    - **栈**:每个线程拥有一个独立的栈,用于存储方法调用帧,包括局部变量表、操作数栈、动态链接、方法出口等信息。 - **方法区**(在Java 8后被元空间取代):存储已加载的类信息、常量、静态变量等。 - **程序...

    JVM运行程序虚拟机的详细介绍.docx

    4. **内存管理**:包括堆内存管理(用于动态对象分配)和栈内存管理(用于方法调用和局部变量)。 JVM的指令集架构有两种主要类型:基于栈和基于寄存器。HotSpot JVM(广泛用于Java服务器)采用基于栈的架构,易于...

    JAVA文件编译执行与虚拟机(JVM)介绍

    - **栈内存(Stack)**:每个线程都有自己的栈空间,用于存储局部变量、方法调用栈帧等。栈内存的空间相对较小,但访问速度快。 - **方法区(Method Area)**:也称为永久代,用于存储类的信息、静态变量、常量池等...

    Java虚拟机-jvm故障诊断与性能优化-源码

    - **栈内存**:每个线程都有一个独立的栈,用于存储方法的局部变量和运算过程。 - **方法区**:存储类信息、常量、静态变量等,现代JVM中通常被替换为元空间。 2. **垃圾收集机制** - **垃圾收集器**:包括...

    hllvm.借HSDB来探索HotSpot VM的运行时数据1

    【描述】:本篇文章旨在探讨Java内存模型中不同类型的变量——包括静态变量t1、实例变量t2和局部变量t3——在HotSpot VM中的存储位置。我们将借助HSDB(HotSpot Serviceability Agent的调试器)来探索实际运行时的...

    JVM-整体结构原理深度解析

    - 局部变量表:存储方法参数和方法内部定义的局部变量。 - 操作数栈:作为计算过程的临时数据存储区。 - 动态链接:在程序运行期间完成符号引用到直接引用的替换。 - 方法返回地址:方法退出后需要返回调用者的...

    jvm调优

    虚拟机栈则为每个线程分配一个独立的栈,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。栈帧的大小直接影响到栈的使用效率,过大的栈帧可能导致栈溢出,而过小则可能限制了并发能力。因此,合理设定栈的...

Global site tag (gtag.js) - Google Analytics