《深入理解JAVA虚拟机》(一) JVM 结构 + 栈帧 详解

1、程序计数器(Program Counter Register)
线程独有,每个线程都有自己的计数器;由于CPU的任意时刻只能执行所有线程中的一条,所以需要使用程序计数器来支持JVM的并发;另外字节码解释器读取下一行指令、分支、循环、跳转、异常处理等等逻辑都依赖于程序计数器。程序计数器是JVM唯一不存在OutOfMemoryError的区域。
2、Java虚拟机栈(Java Virtual Machine Stacks)
线程独有,用于保存线程相关的栈帧(@注释1);生命周期与线程相同,线程中,方法执行的过程等同该方法对应栈帧从入栈到出栈的过程,如果方法执行过程中调用了其它方法,那么同样的道理,方法内部调用的其它方法的栈帧也会入栈,以此类推,直到当前方法执行完,然后从虚拟机栈中出栈即代表一个方法完整的执行过程。
Java虚拟机栈内存大小即可固定,也支持动态拓展(在JVM运行内存的范围内):
(1)当固定大小情况下,线程请求分配的栈容量大于Java虚拟机栈最大容量时,抛出异常:StackOverFlowError。
(2)当可拓展时,如果在拓展过程中,无法申请到足够的内存时,抛出异常:OutOfMemoryError(比如:JVM运行内存被占满,此时已经无处可以申请内存了)。
3、本地方法栈(Native Method Stack)
类似上述Java虚拟机栈,放入Java虚拟机栈中的是Java方法的栈帧;而本地方法栈中的内容是native方法(@注释2)的栈帧,他为native方法服务。
例如:java.lang.String.intern() 和 java.lang.Object.hashCode()方法的定义如下:
public native String intern();
public native int hashCode();
4、Java堆(Java Heap)
Java堆是线程共享的,它是JVM中占用内存空间最大的部分,它的作用是存放Java对象实例,几乎所有的Java对象都从堆中分配内存;因为Java堆存放了大量对象实例,所以这里也是垃圾回收发生的主要场所;Java堆内存大小即可设为固定值,也可以动态拓展,Java堆可以处于非连续的物理内存上(大学的某门课讲过原理,逻辑上连续即可)。
5、方法区(Method Area)
(1) 基本概念:同Java堆,方法区也是线程共享;方法区保存JVM加载的:类的信息、常量、静态变量、即时编译器编译后的代码,等数据;Java虚拟机规划中对方法区限制较少,是否受垃圾回收器管理是可选的,大小可固定或可拓展,可以存在于不联系的内存空间上。
(2) 关于HotSpot虚拟机:Java 7之前,方法区与Java堆共享内存,Java堆被划分为:青年代、老年代、永久代,其中永久代即指方法区,同样此时方法区被垃圾回收器管理;到了Java 8,HotSpot虚拟机改变了实现方式,方法区分配的内存被移至虚拟机外,此时称呼其为元空间,不再与Java堆共享内存,也不再被垃圾回收器管理。
(3) 运行时常量池
a 基本概念:位于方法区,它是每一个类或接口中的,常量池表,运行时的表示形式;每一个运行时常量池(对应 类/接口),在加载 类/接口时被创建并分配到方法区中。
b 常量池作用:存放编译器生成的字面量和符号引用,当虚拟机运行时,从常量池获取字面量或者符号引用,在类创建或者运行时,映射到具体的内存中。
c 字面量(常量):字符串 || final 变量
d 符号引用:类/接口的全限定名 || 字段名称及其描述符 || 方法名称及其描述符
本文仅限于JVM各个组成部分即基本介绍,不深入讨论原理,旨于深入探究JVM前建立相关概念模型。
* 拓展及注释
1、栈帧
基本概念:在JAVA虚拟机栈中使用,用于支持JVM进行方法调用和方法执行的数据结构(栈帧就是虚拟栈中的一个元素),每个方法执行时都会创建一个栈帧,它包括:局部变量表、操作数栈、动态链接、方法返回地址等。对于JVM执行引擎来说,在活动的线程中,只有处于栈顶的栈帧是有效的,所有字节码指令都只对作用在当前栈帧关联的方法上。
(1)局部变量表:一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量,局部变量表的容量以 变量槽(Slot) 为单位;每个Slot的大小与JVM平台有关,一般可以存放32位以内的数据类型:8种基本数据类型,以及 对象应用(reference)和returnAddress(方法返回地址)。对于 对象应用(reference),JVM必须做到能够直接或间接查找到对象在JAVA堆中的数据存储地址起始索引,以及能查找到对象所属数据类型在方法区中存储的类型信息;
(2)操作数栈:它是一个LIFO栈,操作数栈中的每一个元素都可以是任意的Java数据类型,栈的最小容量单位为32bite,32位的数据类型栈一个容量单位,64位数据类型占2个容量单位。JVM引擎执行的实质可以理解成:从操作数栈栈顶提取元素,然后执行指令,并把执行结果压入栈中的过程。
(3)动态链接:
前提条件(背景):每一个栈帧都会持有一个指向运行时常量池中该栈帧所属的方法引用,持有该引用的目的是为了支持方法调用过程中的动态链接。字节码(Class文件中,可以借助工具查看编译后的Class文件中的字节码指令) 的方法调用指令就是以Class文件常量池中的符号引用作为参数的;
动态链接:上述符号引用中,有的符号引用会在每次运行期间转化为直接引用,该类引用称为动态链接;
静态链接:相较于动态链接,对于静态链接,符号引用会在类加载 或 第一次使用的时候就转化为直接引用,而不是每次执行时去转化为直接引用。
(4)方法返回地址:方法执行完有两种方式退出方法,其一为方法执行过程中遇到任意一个方法返回的字节码指令,然后将相关结果返回给方法的调用者。其二,方法执行碰到异常,并且异常没有被捕获处理时推出方法。方法退出,即代表当前 (栈)帧 出栈,后续将会执行当前方法的调用者(上层方法)所对应的 (栈)帧 后续指令,那么对应的操作即为:恢复调用者对应 栈帧 的局部变量表和操作数栈,把当前方法的返回值压入调用者方法对应栈帧的操作数栈中,调用PC计数器的值并使之执行当前方法调用指令的下一条指令(当前方法执行完,挫骨扬灰,只留下了舍利供调用者使用,当然也可能直接烧成灰了啥也没有(无返回值))。
2、native方法(本地方法):
可以理解为java调用非Java代码实现的方法,的接口。众所周知,Java中很多东西都是其它语言实现的(例如C语言),使用native方法(本地方法)的原因如下:
(1)与java环境外交互:
有时java应用需要与java外面的环境交互。这是本地方法存在的主要原因,你可以想想java需要与一些底层系统交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解java应用之外的繁琐的细节。
(2)与操作系统交互:
JVM支持着java语言本身和运行时库,它是java程序赖以生存的平台,它由一个解释器(解释字节码)和一些连接到本地代码的库组成。然而不管怎 样,它毕竟不是一个完整的系统,它经常依赖于一些底层(underneath在下面的)系统的支持。这些底层系统常常是强大的操作系统。通过使用本地方法,我们得以用java实现了jre的与底层系统的交互,甚至JVM的一些部分就是用C写的,还有,如果我们要使用一些java语言本身没有提供封装的操作系统特性时,我们也需要使用本地方法。
《深入理解JAVA虚拟机》(一) JVM 结构 + 栈帧 详解的更多相关文章
- 【转载】深入理解Java虚拟机笔记---运行时栈帧结构
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法的局部变量表,操作 ...
- 深入理解java虚拟机(八)类加载过程详解
类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(In ...
- 《深入理解Java虚拟机:JVM高级特性与最佳实践》【PDF】下载
<深入理解Java虚拟机:JVM高级特性与最佳实践>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062566 内容简介 作为一位 ...
- 读书笔记-《深入理解Java虚拟机:JVM高级特性与最佳实践》
目录 概述 第一章: 走进Java 第二章: Java内存区域与内存溢出异常 第三章: 垃圾收集器与内存分配策略 第四章: 虚拟机性能监控与故障处理 第五章: 调优案例分析与实战 第六章: 类文件结构 ...
- Java中堆内存和栈内存详解2
Java中堆内存和栈内存详解 Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,ja ...
- 深入理解Java虚拟机之JVM垃圾回收随笔
1.对象已经死亡? 1.1引用计数法:给对象中添加一个引用计数器,每当有一个地方引用他时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用 的.但是它很难解决 ...
- Java 虚拟机系列二:垃圾收集机制详解,动图帮你理解
前言 上篇文章已经给大家介绍了 JVM 的架构和运行时数据区 (内存区域),本篇文章将给大家介绍 JVM 的重点内容--垃圾收集.众所周知,相比 C / C++ 等语言,Java 可以省去手动管理内存 ...
- java虚拟机规范-运行时栈帧
前言 java虚拟机是java跨平台的基石,本文的描述以jdk7.0为准,其他版本可能会有一些微调. 引用 java虚拟机规范 java虚拟机规范-运行时数据区 java内存运行时的栈帧结构 java ...
- 深入理解Java虚拟机:JVM高级特性与最佳实践
第一部分走近Java第1章走近Java21.1概述21.2Java技术体系31.3Java发展史51.4Java虚拟机发展史91.4.1SunClassicExactVM91.4.2SunHotSpo ...
- 深入理解Java虚拟机之JVM内存布局篇
内存布局**** JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JVM的稳定高效运行.不同的JVM对于内存的划分方式和管理机制存在部分差异.结合JVM虚拟机规范,一起来 ...
随机推荐
- 阿里云IPV6 创建虚拟机的过程
阿里云IPV6 创建虚拟机的过程 背景 IPV6 已经越来越广泛的应用. 想在外网开通一下IPV6,发现还有一些坑. 这里总结一下. 备忘. 开通方式 1. 登录阿里云的控制台, 打开云服务器ECS的 ...
- 【转帖】【笔记】python连接神通数据库
https://www.cnblogs.com/wyongbo/p/17054924.html python连接国产神州通用数据库. 一.准备 下载whl及dll: 链接: https://pan.b ...
- [转帖]解读内核 sysctl 配置中 panic、oops 相关项目
写在前面 本篇文章的内容主要来自内核源码树 Documentation/admin-guide/sysctl/kernel.rst文件. softlockup vs hardlockup softlo ...
- Nginx调优总结-第六部分编译优化与简单测试
第六部分 编译优化 Nginx可以自行编译,所以里面可以设置多个编译策略. 也可以自行修改源码,便于比如进行ip_hash的全IP地址验证. 也可以修改nginx的版本号等信息, 避免内发现. 还可以 ...
- linux线程调度策略
linux线程调度策略 这是一篇非常好的关于线程调度的资料,翻译自shed 目录 linux线程调度策略 Scheduling policies SCHED_FIFO: First in-first ...
- 30岁程序媛求职路复盘:文转码+失业半年+PHP如何涨薪5K!?
这篇文章来自一位群友的分享: 这篇文章写于下班路上,刚刚入职不久,我想再冲刺一下大厂,阳哥建议我坚持总结打卡,可以尝试写写博客. 那我就从这篇开始吧,希望开个好头! 上班的感觉真好 今天是入职的第二周 ...
- rust入坑指南之ownership
作者:京东零售 王梦津 I. 前言 Rust,不少程序员的白月光,这里我们简单罗列一些大牛的评价. Linus Torvalds:Linux内核的创始人,对Rust的评价是:"Rust的主要 ...
- 浅谈kafka
作者:京东科技 徐拥 入门 1.什么是kafka? apache Kafka is a distributed streaming platform. What exactly dose that m ...
- vue中$once的使用
$once 可以给组件实例绑定一个自定义事件,但该事件只能被触发一次,触发之后随即被移除 $once的简单使用 <template> <div> <button @cli ...
- 手写Promise自定义封装 then 函数
Promise 自定义封装 then 函数 <script src="./Promise.js"></script> <script type=&qu ...