热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVM:运行时数据区虚拟机栈

2.2虚拟机栈2.1.1概述优点:跨平台,指令集小,编译器容易实现缺点:性能下降实现同样的工能需要更多的指令集栈是运
2.2虚拟机栈

2.1.1概述

优点:跨平台,指令集小,编译器容易实现

缺点:性能下降实现同样的工能需要更多的指令集

栈是运行时的单位,而堆是存储的单元

是什么?

每个线程在创建是辉创建一个虚拟机栈,其内部保存的一个个栈帧队里着一次次的java方法的调用。

生命周期:与线程一致。

作用:

主管java程序的运行,他保持方法的局部变量,部分结果,并参与方法的调用和返回。

特点:

快,仅次于pc寄存器,无gc,可能oom。只能进出栈两个操作。

栈的异常

stackoverflowerror 死循环

线程请求分配栈容量超过java虚拟机栈的最大容量。

outofmerroryerror动态扩增无法申请足够的内容(内存不足)

设置栈的大小 -Xss

2.1.2栈帧

栈帧中存储什么?

每个线程都有自己的栈,栈中的数据都是以栈帧的格式存在

在这个线程上正在执行的每个方法对应一个栈帧

栈帧是一个内存块,是一个数据集,维系方法执行过程中的各种数据信息。

栈运行原理:

jvm对java栈只有压栈和出栈

一条活动线程中,一个时带点只能有一个正在执行方法的栈帧,称为当前栈帧,其方法为当前方法,对饮的类为当前类。

执行引擎运行的所有字节码指令只对当前栈帧进行操作。

如果当前方法调用的了新的方法,就会创建新方法的栈帧,放在栈顶。

弹出栈帧的两个方式:

return正常函数返回,或者抛出异常。

2.1.3栈的内部结构

每个栈帧中存储:

局部变量表

操作数栈

动态链接

方法返回地址

一些附加信息

局部变量表

局部变量数据或本地变量表

用于存储方法参数和定义字啊方法体内的局部变量

关于slot的理解

局部变量表的最基本单元

32位以内的占用一个slot,64位的占用两个

long和double调用使用第一个索引,

实例方法,即非静态的方法,会那么该对象引用this将会存放在index为0的slot处,其余的参数按照参数表顺序继续排列。

静态方法中没有this,不在静态变量表中存在。

局部变量表中的空间可重复利用,因为出了作用域被销毁,下面的变量即可复用。

静态变量和局部变量对比

参数表分配完毕之后,再根据方法体内定义的变量的顺序和作用域分配。

我们知道类变量表有两次初始化的机会,第一次是在==“准备阶段”,执行系统初始化,对类变量设置零值,另一次则是在“初始化”==阶段,赋予程序员在代码中定义的初始值。

和类变量初始化不同的是,局部变量表不存在系统初始化的过程,这意味着一旦定义了局部变量则必须人为的初始化,否则无法使用。

局部变量一定要显示赋值才能被使用。

  • 在栈帧中,与性能调优关系最为密切的部分就是前面提到的局部变量表。在方法执行时,虚拟机使用局部变量表完成方法的传递。
  • 局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收。

2.1.4操作数栈


  • 操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
    操作数栈的深度,看同时操作了多少个变量。

2.1.5栈顶缓存技术

前面提过,基于栈式架构的虚拟机所使用的零地址指令更加紧凑,但完成一项操作的时候必然需要使用更多的入栈和出栈指令,这同时也就意味着将需要更多的指令分派(instruction dispatch)次数和内存读/写次数。

由于操作数是存储在内存中的,因此频繁地执行内存读/写操作必然会影响执行速度。为了解决这个问题,HotSpot JVM的设计者们提出了栈顶缓存(Tos,Top-of-Stack Cashing)技术,将栈顶元素全部缓存在物理CPU的寄存器中,以此降低对内存的读/写次数,提升执行引擎的执行效率

2.1.6动态链接

动态链接、方法返回地址、附加信息 : 有些地方被称为帧数据区

每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking),比如:invokedynamic指令

在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。

其实:字节码指令中#对应常量池中的代码。#为符号引用,边上的为真实引用。

为什么需要运行时常量池呢?

常量池的作用:就是为了提供一些符号和常量,便于指令的识别.

方法的调用:

在JVM中,将符号引用转换为调用方法的直接引用与方法的绑定机制相关.

多个栈帧可以调用项目的常量池中的引用

静态链接:

当一个字节码文件被装载进JVM内部时,如果被调用的目标方法在编译期可知,且运行期保持不变时,这种情况下降调用方法的符号引用转换为直接引用的过程称之为静态链接

动态链接:

如果被调用的方法在编译期无法被确定下来,只能够在程序运行期将调用的方法的符号转换为直接引用,由于这种引用转换过程具备动态性,因此也被称之为动态链接。

早期绑定:

早期绑定就是指被调用的目标方法如果在编译期可知,且运行期保持不变时,即可将这个方法与所属的类型进行绑定,这样一来,由于明确了被调用的目标方法究竟是哪一个,因此也就可以使用静态链接的方式将符号引用转换为直接引用。

使用super.eat方法,就是调用明确的父类的方法,就是早期绑定。

晚期绑定

如果被调用的方法在编译期无法被确定下来,只能够在程序运行期根据实际的类型绑定相关的方法,这种绑定方式也就被称之为晚期绑定。

比如接口中的方法,或者方法参数为对象的方法调用。

一般高级语言都有早期绑定和晚期绑定。封装继承多态的出现

虚方法和非虚方法

  • 如果方法在编译期就确定了具体的调用版本,这个版本在运行时是不可变的。这样的方法称为非虚方法。
  • 静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法。其他方法称为虚方法。

普通调用指令:

  • invokestatic:调用静态方法,解析阶段确定唯一方法版本
  • invokespecial:调用方法、私有及父类方法,解析阶段确定唯一方法版本
  • invokevirtual:调用所有虚方法
  • invokeinterface:调用接口方法

动态调用指令:

  • invokedynamic:动态解析出需要调用的方法,然后执行

需要借助ASM这种底层字节码工具来产生invokedynamic指令。直到Java8的Lambda表达式的出现,invokedynamic指令的生成,在Java中才有了直接的生成方式。

动态类型语言和静态类型语言

动态类型语言和静态类型语言两者的区别就在于对类型的检查是在编译期还是在运行期,满足前者就是静态类型语言,反之是动态类型语言。

说的再直白一点就是,静态类型语言是判断变量自身的类型信息;动态类型语言是判断变量值的类型信息,变量没有类型信息,变量值才有类型信息,这是动态语言的一个重要特征。

Java是静态类型语言(尽管lambda表达式为其增加了动态特性),js,python是动态类型语言

lambda表达式实现

illegalAccesserror

没有权限访问,重复加载maven的jar包,

虚方法表:

为了提高性能,JVM采用在类的方法区建立一个虚方法表 (virtual method table)(非虚方法不会出现在表中)来实现。使用索引表来代替查找。

每个类一个虚方法表。

2.1.7方法返回地址

存放调用该方法的pc寄存器的值。

调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。

当一个方法开始执行后,只有两种方式可以退出这个方法:

在字节码指令中,返回指令包含ireturn(当返回值是boolean,byte,char,short和int类型时使用),lreturn(Long类型),freturn(Float类型),dreturn(Double类型),areturn。另外还有一个return指令声明为void的方法,实例初始化方法,类和接口的初始化方法使用。

一些附加信息

栈帧中还允许携带与Java虚拟机实现相关的一些附加信息。例如:对程序调试提供支持的信息。

面试题:

栈溢出的情况?栈溢出:StackOverflowError

栈中是不存在GC的,存在OOM和StackOverflowError

举个简单的例子:在main方法中调用main方法,就会不断压栈执行,直到栈溢出;

栈的大小可以是固定大小的,也可以是动态变化(动态扩展)的

如果是固定的,那么会抛出StackOverflowError

如果是动态扩展的,那么会抛出OOM异常(java.lang.OutOfMemoryError)

举例栈溢出的情况?(StackOverflowError)

通过 -Xss设置栈的大小

调整栈大小,就能保证不出现溢出么?

不能。因为调整栈大小,只会减少出现溢出的可能,栈大小不是可以无限扩大的,所以不能保证不出现溢出

分配的栈内存越大越好么?

不是,一定时间内降低了OOM概率,但是会挤占其它的线程空间,因为整个空间是有限的。

垃圾回收是否涉及到虚拟机栈?

不会;垃圾回收只会涉及到方法区和堆中,方法区和堆也会存在溢出的可能

程序计数器,只记录运行下一行的地址,不存在溢出和垃圾回收

虚拟机栈和本地方法栈,都是只涉及压栈和出栈,可能存在栈溢出,不存在垃圾回收

方法中定义的局部变量是否线程安全?

具体问题具体分析。如果对象是在内部产生,并在内部消亡,没有返回到外部,那么它就是线程安全的,反之则是线程不安全的。


推荐阅读
  • 探索 PHP 8.0 的重大更新:轻松获取年度月份数据
    PHP 8.0 引入了多项重要更新,包括增强的类型系统、全新的 JIT 编译器以及联合类型等特性。这些改进不仅提升了性能,还简化了开发流程。本文将重点介绍如何利用 PHP 8.0 的新功能轻松获取年度和月份数据,为开发者提供更高效、更简洁的解决方案。 ... [详细]
  • Python与R语言在功能和应用场景上各有优势。尽管R语言在统计分析和数据可视化方面具有更强的专业性,但Python作为一种通用编程语言,适用于更广泛的领域,包括Web开发、自动化脚本和机器学习等。对于初学者而言,Python的学习曲线更为平缓,上手更加容易。此外,Python拥有庞大的社区支持和丰富的第三方库,使其在实际应用中更具灵活性和扩展性。 ... [详细]
  • 从无到有,构建个人专属的操作系统解决方案
    操作系统(OS)被誉为程序员的三大浪漫之一,常被比喻为计算机的灵魂、大脑、内核和基石,其重要性不言而喻。本文将详细介绍如何从零开始构建个人专属的操作系统解决方案,涵盖从需求分析到系统设计、开发与测试的全过程,帮助读者深入理解操作系统的本质与实现方法。 ... [详细]
  • 全面解析Java虚拟机:内存模型深度剖析 ... [详细]
  • 深度森林算法解析:特征选择与确定能力分析
    本文深入探讨了深度森林算法在特征选择与确定方面的能力。提出了一种名为EncoderForest(简称eForest)的创新方法,作为首个基于决策树的编码器模型,它在处理高维数据时展现出卓越的性能,为特征选择提供了新的视角和工具。 ... [详细]
  • 《软件测试精要》深度解析与实战经验分享
    《软件测试精要》深度解析与实战经验分享,系统梳理了软件测试的核心概念与关键原则,结合实际项目中的测试经验和教训,详细探讨了测试分类、测试权衡要素、测试效率、测试覆盖率以及测试框架的引入和用例设计等内容,为读者提供了全面而实用的指导。 ... [详细]
  • 全功能键盘主机:老卫搞机090期深度解析键盘与主机的完美融合
    全功能键盘主机:老卫搞机090期深度解析键盘与主机的完美融合 ... [详细]
  • Java中高级工程师面试必备:JVM核心知识点全面解析
    对于软件开发人员而言,随着技术框架的不断演进和成熟,许多高级功能已经被高度封装,使得初级开发者只需掌握基本用法即可迅速完成项目。然而,对于中高级工程师而言,深入了解Java虚拟机(JVM)的核心知识点是必不可少的。这不仅有助于优化性能和解决复杂问题,还能在面试中脱颖而出。本文将全面解析JVM的关键概念和技术细节,帮助读者全面提升技术水平。 ... [详细]
  • 在处理大规模并发请求时,传统的多线程或多进程模型往往无法有效解决性能瓶颈问题。尽管它们在处理小规模任务时能提升效率,但在高并发场景下,系统资源的过度消耗和上下文切换的开销会显著降低整体性能。相比之下,Python 的 `asyncio` 模块通过协程提供了一种轻量级且高效的并发解决方案。本文将深入解析 `asyncio` 模块的原理及其在实际应用中的优化技巧,帮助开发者更好地利用协程技术提升程序性能。 ... [详细]
  • 在当前各种算法实现和开源软件包层出不穷的背景下,算法对程序员的重要性是否有所减弱?回顾历史,早期程序员必须熟练掌握算法并频繁自行编写。然而,随着技术的发展,算法逐渐成为一种“商品”,现代开发者更多依赖现成的库和商业算法解决方案。有观点认为,机器学习领域中,许多算法已经被高度封装,不再需要深入理解其背后的数学原理。然而,这种趋势也引发了关于技术深度与广度平衡的讨论,强调了基础理论知识在应对复杂问题时的不可替代性。 ... [详细]
  • 2019年后蚂蚁集团与拼多多面试经验详述与深度剖析
    2019年后蚂蚁集团与拼多多面试经验详述与深度剖析 ... [详细]
  • 深入解析Java虚拟机内存模型(JMM)及其核心机制
    为了深入理解Java内存模型(JMM),首先需要对计算机硬件体系有全面的认识,尤其是CPU与主存之间的多级缓存架构。这些硬件特性直接影响了JMM的设计和实现,确保在多线程环境下数据的一致性和可见性。 ... [详细]
  • Django框架下的对象关系映射(ORM)详解
    在Django框架中,对象关系映射(ORM)技术是解决面向对象编程与关系型数据库之间不兼容问题的关键工具。通过将数据库表结构映射到Python类,ORM使得开发者能够以面向对象的方式操作数据库,从而简化了数据访问和管理的复杂性。这种技术不仅提高了代码的可读性和可维护性,还增强了应用程序的灵活性和扩展性。 ... [详细]
  • 在 Linux 系统中,`/proc` 目录实现了一种特殊的文件系统,称为 proc 文件系统。与传统的文件系统不同,proc 文件系统主要用于提供内核和进程信息的动态视图,通过文件和目录的形式呈现。这些信息包括系统状态、进程细节以及各种内核参数,为系统管理员和开发者提供了强大的诊断和调试工具。此外,proc 文件系统还支持实时读取和修改某些内核参数,增强了系统的灵活性和可配置性。 ... [详细]
  • 六个接私活的平台,技术在手,财富自由!值得推荐给每一位专业人士!
    本文将介绍六个适合专业人士接私活的平台,帮助技术人才实现财富自由。这些平台不仅提供了丰富的项目机会,还为用户搭建了高效的合作桥梁,是每位技术人士不容错过的资源。 ... [详细]
author-avatar
pupupupupupupupupu
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有