JVM指令介绍
栈和局部变量操作
1、常量入栈操作
--入栈操作三种方式指明常量的值:常量值隐式包含在操作码内部,常量值在字节码流中如同操作数一样紧随在操作码之后,或者从常量池中取出常量
--Java栈中每一个位置的长度都是一个字长(至少32位宽)
--Java源代码中所有的字符串文字最终都作为入口存储与常量池中。如果同一个应用程序的多个类都使用同样的字符串文字,按摩此字符串文字将在使用它的所有类的class文件中出现。但Java虚拟机会把所有具有相同字符顺序的字符串文字处理为同一个String对象,只会创建一个相应的String对象来表示所有的字符串文字。
备注:要深刻理解源代码中字符串文字与JVM生成的String对象的差别。
--wide指令:跳转指令并不允许直接跳转到被wide指令修改过的操作码。
类型转换
1、设计 byte short和char类型的运算操作首先会把这些值转换为int类型,然后对int类型值进行运算,最后得到int类型的结果。
整数运算
--取余运算 irem 取反运算 ineg
逻辑运算
-- "<<" ishl 向左移位
">>" ishr 向右移位
">>>" iushr 逻辑移位
浮点运算
1、在Java虚拟机中,浮点运算基于32位float类型和64位double类型进行。浮点数由符号、尾数、基数和指数四部分组成。
2、float类型的格式如下,符号位表示为s,指数位表示为e,尾数位表示为m:指数占8位,double类型中占11位
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
指数位的解释有三种方式:
指数位全为1,表示该数为乘法或者减法所产生的特殊值之一——无穷大或者非数字(NaN),NaN是某种特殊操作,诸如0除以0的结果。
指数位全是0,表示该数是一个非规范化浮点数。
其他类型的指数位表示该数十一个规范化的浮点数。
3、float类型的特殊值
指数位全为1而且尾数位全为0,表示无穷大
指数位全为1,尾数位不全为0,表示该数为"NaN":Java虚拟机总是为NaN产生同样的尾数,除了尾数中最高有效位为1外,其余全为0
4、2的幂指数的确定
指数位既不全为0,也不全为1,该数即为规范化的浮点数。可以通过把指数看成一个整数,然后减去一个偏移量来确定2的幂指数。对于float类型,偏移量是127,double类型是1023
浮点数表示参考文章:http://apps.hi.baidu.com/share/detail/30212277
对象和数组
1、只有对象引用和基本类型可以在Java的栈中以局部变量形式存在,Java栈中不能容纳对象。
2、针对数组的操作码
--newarray操作码用来创建基本类型的数组
--anewarray/multianewarray操作码用来创建对象引用的数组
finally子句
--jsr指令是使Java虚拟机跳转到微型子例程的操作码。
--ret指令的功能是执行从子例程中返回的操作。
jsr指令会把返回地址压入栈,在执行子例程开始时又会把返回地址弹出栈,然后存入局部变量中。当finally执行结束后(此结束是指finally执行到了最后一条语句正常执行完毕,不包括抛出异常,或执行return、continue、break等情况),ret指令从局部变量里取出返回地址,然后返回。如果finally子句非正常结束,则根本不会执行ret指令,这也是为什么返回地址要从栈中弹出并存到局部变量的原因,否则如果ret指令不执行该地址将存在在栈中而不会被用到。
--Java虚拟机在finally子句执行完毕钱返回一个值的方式:
虽然finally改变了i的值,但是虚拟机仍会返回执行finally子句前的i的值;如果需要finally子句改变返回值,就不得不在finally子句里加一个return语句,用来返回被finally子句更新过的值(如下代码)。
方法的调用和返回
1、对于实例方法,使用invokevirtual指令;对于类方法,使用invokestatic指令。
2、方法调用的过程描述
3、通常使用invokevirtual指令调用实例方法,但在某些特定的情况下,也会使用另外两种操作码——invokespecial和invokeinterface。
--当根据引用的类型来调用实例方法,而不是根据对象的类来调用的时候,通常使用invokespecial指令。分三种情况:
1)实例初始化(<init>())方法
2)私有方法
3)使用super关键字所调用的方法
--当给出一个接口的引用时,使用invokeinterface来调用一个实例方法
备注:invokevirtual只能调用当前类的方法,无法使用超类的方法。——对象初始化时在调用自身的<init>()的时候会首先调用父类的<init>()方法,而invokespecial可以完成这样的操作。
释疑: 什么是根据引用的类型,什么是根据对象的类(参考下面的代码可知)
情况一:
运行结果: super class say hi
解释:main方法中创建的对象类型是SubClass,但SubClass的sayHi()方法是从SuperClass继承过来的,所以此时引用的类型其实是SuperClass,而invokespecial根据引用的类型来调用实例方法,即SuperClass的speakHi()。且此时所调用方法为私有方法。
情况二:
运行结果: sub class say hi
解释:SubClass覆盖了SuperClass的sayHi()方法,此时不仅创建的对象类型是SubClass,真正引用的类型也是,所以调用的就是SubClass的speakHi()。这也从另一面证明了第一种情况的正确性。
--Java虚拟机使用不同于类引用(invokevirtual)的操作码来调用接口引用(invokeinterface)的方法,这是因为Java不能像使用类引用那样,使用许多和方法表偏移量相关的假设。对于类引用来说,无论对象实际的类是什么,方法在方法表中始终占据相同的位置,但对于接口引用来说,情况就不是这样了,位于不同类中的同一方法所占据的位置是不同的,尽管这些类实现同一个接口。
线程同步
1、Java使用的同步机制是监视器。
--一个线程只有在它正持有监视器时才能执行等待命令,而且它只能通过再次成为监视器的持有者才能离开等待区。
2、对象锁
--堆和方法区是被所有线程共享的,Java程序需要为两种多线程访问数据进行协调:
保存在堆中的实例变量
保存在方法区中的类变量
3、可重入锁:一个线程可以允许多次对同一个对象上锁。对于每一个对象来说,Java虚拟机维护一个计数器,记录对象被加了多少次锁。没有被锁的对象的计数器是0,当一个线程第一次获得锁的时候计数跳到1,线程没加锁一次,计数器就加1,(只有已经拥有了这个对象的锁的线程才能对该对象再次加锁。在它释放锁之前,其他的线程不能对这个对象加锁)每当线程释放锁一次,计数器就减1,当计数器跳到0的时候,锁就完全被释放了,其他的线程才可以使用它。
栈和局部变量操作
1、常量入栈操作
--入栈操作三种方式指明常量的值:常量值隐式包含在操作码内部,常量值在字节码流中如同操作数一样紧随在操作码之后,或者从常量池中取出常量
--Java栈中每一个位置的长度都是一个字长(至少32位宽)
--Java源代码中所有的字符串文字最终都作为入口存储与常量池中。如果同一个应用程序的多个类都使用同样的字符串文字,按摩此字符串文字将在使用它的所有类的class文件中出现。但Java虚拟机会把所有具有相同字符顺序的字符串文字处理为同一个String对象,只会创建一个相应的String对象来表示所有的字符串文字。
备注:要深刻理解源代码中字符串文字与JVM生成的String对象的差别。
--wide指令:跳转指令并不允许直接跳转到被wide指令修改过的操作码。
类型转换
1、设计 byte short和char类型的运算操作首先会把这些值转换为int类型,然后对int类型值进行运算,最后得到int类型的结果。
整数运算
--取余运算 irem 取反运算 ineg
逻辑运算
-- "<<" ishl 向左移位
">>" ishr 向右移位
">>>" iushr 逻辑移位
浮点运算
1、在Java虚拟机中,浮点运算基于32位float类型和64位double类型进行。浮点数由符号、尾数、基数和指数四部分组成。
2、float类型的格式如下,符号位表示为s,指数位表示为e,尾数位表示为m:指数占8位,double类型中占11位
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
指数位的解释有三种方式:
指数位全为1,表示该数为乘法或者减法所产生的特殊值之一——无穷大或者非数字(NaN),NaN是某种特殊操作,诸如0除以0的结果。
指数位全是0,表示该数是一个非规范化浮点数。
其他类型的指数位表示该数十一个规范化的浮点数。
3、float类型的特殊值
指数位全为1而且尾数位全为0,表示无穷大
指数位全为1,尾数位不全为0,表示该数为"NaN":Java虚拟机总是为NaN产生同样的尾数,除了尾数中最高有效位为1外,其余全为0
4、2的幂指数的确定
指数位既不全为0,也不全为1,该数即为规范化的浮点数。可以通过把指数看成一个整数,然后减去一个偏移量来确定2的幂指数。对于float类型,偏移量是127,double类型是1023
浮点数表示参考文章:http://apps.hi.baidu.com/share/detail/30212277
对象和数组
1、只有对象引用和基本类型可以在Java的栈中以局部变量形式存在,Java栈中不能容纳对象。
2、针对数组的操作码
--newarray操作码用来创建基本类型的数组
--anewarray/multianewarray操作码用来创建对象引用的数组
finally子句
--jsr指令是使Java虚拟机跳转到微型子例程的操作码。
--ret指令的功能是执行从子例程中返回的操作。
jsr指令会把返回地址压入栈,在执行子例程开始时又会把返回地址弹出栈,然后存入局部变量中。当finally执行结束后(此结束是指finally执行到了最后一条语句正常执行完毕,不包括抛出异常,或执行return、continue、break等情况),ret指令从局部变量里取出返回地址,然后返回。如果finally子句非正常结束,则根本不会执行ret指令,这也是为什么返回地址要从栈中弹出并存到局部变量的原因,否则如果ret指令不执行该地址将存在在栈中而不会被用到。
--Java虚拟机在finally子句执行完毕钱返回一个值的方式:
public class Test4 { public int test(){ int i = 0; try{ i = 1; return i; }finally{ i = 2; } } public static void main(String[] args) { Test4 t = new Test4(); System.out.println(t.test()); } } --------------------- i = 1
虽然finally改变了i的值,但是虚拟机仍会返回执行finally子句前的i的值;如果需要finally子句改变返回值,就不得不在finally子句里加一个return语句,用来返回被finally子句更新过的值(如下代码)。
public class Test4 { public int test(){ int i = 0; try{ i = 1; }finally{ i = 2; return i; } } public static void main(String[] args) { Test4 t = new Test4(); System.out.println(t.test()); } } --------------------- i = 2
方法的调用和返回
1、对于实例方法,使用invokevirtual指令;对于类方法,使用invokestatic指令。
2、方法调用的过程描述
3、通常使用invokevirtual指令调用实例方法,但在某些特定的情况下,也会使用另外两种操作码——invokespecial和invokeinterface。
--当根据引用的类型来调用实例方法,而不是根据对象的类来调用的时候,通常使用invokespecial指令。分三种情况:
1)实例初始化(<init>())方法
2)私有方法
3)使用super关键字所调用的方法
--当给出一个接口的引用时,使用invokeinterface来调用一个实例方法
备注:invokevirtual只能调用当前类的方法,无法使用超类的方法。——对象初始化时在调用自身的<init>()的时候会首先调用父类的<init>()方法,而invokespecial可以完成这样的操作。
释疑: 什么是根据引用的类型,什么是根据对象的类(参考下面的代码可知)
情况一:
public class SuperClass{ private void speakHi(){ System.out.println("super class say hi"); } void sayHi(){ speakHi(); } } public class SubClass extends SuperClass{ void speakHi(){ System.out.println("sub class say hi"); } public static void main(String[] args){ SubClass ins = new SubClas(); ins.sayHi(); } }
运行结果: super class say hi
解释:main方法中创建的对象类型是SubClass,但SubClass的sayHi()方法是从SuperClass继承过来的,所以此时引用的类型其实是SuperClass,而invokespecial根据引用的类型来调用实例方法,即SuperClass的speakHi()。且此时所调用方法为私有方法。
情况二:
public class SuperClass{ private void speakHi(){ System.out.println("super class say hi"); } void sayHi(){ speakHi(); } } public class SubClass extends SuperClass{ void speakHi(){ System.out.println("sub class say hi"); } void sayHi(){ speakHi(); } public static void main(String[] args){ SubClass ins = new SubClas(); ins.sayHi(); } }
运行结果: sub class say hi
解释:SubClass覆盖了SuperClass的sayHi()方法,此时不仅创建的对象类型是SubClass,真正引用的类型也是,所以调用的就是SubClass的speakHi()。这也从另一面证明了第一种情况的正确性。
--Java虚拟机使用不同于类引用(invokevirtual)的操作码来调用接口引用(invokeinterface)的方法,这是因为Java不能像使用类引用那样,使用许多和方法表偏移量相关的假设。对于类引用来说,无论对象实际的类是什么,方法在方法表中始终占据相同的位置,但对于接口引用来说,情况就不是这样了,位于不同类中的同一方法所占据的位置是不同的,尽管这些类实现同一个接口。
线程同步
1、Java使用的同步机制是监视器。
--一个线程只有在它正持有监视器时才能执行等待命令,而且它只能通过再次成为监视器的持有者才能离开等待区。
2、对象锁
--堆和方法区是被所有线程共享的,Java程序需要为两种多线程访问数据进行协调:
保存在堆中的实例变量
保存在方法区中的类变量
3、可重入锁:一个线程可以允许多次对同一个对象上锁。对于每一个对象来说,Java虚拟机维护一个计数器,记录对象被加了多少次锁。没有被锁的对象的计数器是0,当一个线程第一次获得锁的时候计数跳到1,线程没加锁一次,计数器就加1,(只有已经拥有了这个对象的锁的线程才能对该对象再次加锁。在它释放锁之前,其他的线程不能对这个对象加锁)每当线程释放锁一次,计数器就减1,当计数器跳到0的时候,锁就完全被释放了,其他的线程才可以使用它。
发表评论
-
深入JVM学习笔记 垃圾收集
2011-11-16 10:25 804垃圾收集 1、垃圾收集 ... -
深入JVM学习笔记 连接模型
2011-11-15 14:13 970连接模型 1、Class文件 ... -
深入JVM学习笔记 类型生命周期
2011-11-10 12:18 890Java Class文件 1、Class文件中的内容结构列表 ... -
深入JVM学习笔记 JVM是什么
2011-11-04 10:58 1210JVM是什么 1、守护和非 ... -
深入JVM学习笔记 Java技术体系结构
2011-11-01 14:43 9681、不同Java虚拟机执行 ... -
GridBagLayout的一篇好文章
2011-10-28 16:36 816http://www.cnblogs.com/willmove ... -
JVM书籍保存
2011-10-25 17:05 1680JVM的经典书籍 -
Java继承与上溯 深入理解
2011-10-12 16:21 728Java继承与上溯: Child继承Parent,子类的变量和 ... -
java并发学习 读书笔记二
2011-09-13 10:26 843Executor框架介绍 1、Execu ... -
Andorid教程视频
2011-07-18 13:39 554http://v.youku.com/v_playlist/f ... -
近期面试学习
2011-06-15 21:49 643面试题目Java方面记载: ... -
设计模式 经典书籍
2011-04-30 00:09 980GoF的著作《设计模式——可复用面向对象软件的基础》 有机会 ... -
2011.03.07 Java泛型通配符
2011-03-07 20:36 7091、通配符提供了使用的 ... -
2011.03.07 Java泛型通配符
2011-03-07 20:26 675先看看Collection库中的几个方法。 public i ... -
每日学习:2011.3.4 Java泛型
2011-03-04 12:08 7061、在泛型代码内部,无 ... -
每日学习:2011.3.3 闭包 动态代理 Map的key
2011-03-03 15:05 6621、闭包:是一个可调用的对象,它记录了一些信息,这些信息来自于 ...
相关推荐
### JVM学习笔记(一) #### 一、JVM概述与工具使用 JVM(Java Virtual Machine)是Java语言的核心组成部分之一,它为Java程序提供了一个跨平台的运行环境。本篇学习笔记主要介绍如何利用一系列工具来查看和监控JVM...
### JVM学习笔记 #### JVM内存模型 (JMM) JVM内存模型主要分为以下几个部分: - **Java堆**:这是所有线程共享的一块区域,在虚拟机启动时创建。主要用于存放对象实例,几乎所有的对象实例都在这里分配内存。 - *...
### 深入解析 JVM 内存区域 #### 一、Java内存区域概述 Java虚拟机(JVM)作为Java程序的运行环境,负责管理和分配内存...理解这些区域对于深入学习JVM原理至关重要,也有助于开发者编写更加高效和可靠的Java应用程序。
### JVM中篇笔记知识点 #### 一、Class 文件结构 ##### 1. 概述 - **字节码文件的跨平台性** - Java 的跨平台性体现在 "Write Once, Run Anywhere" (WORA),即一次编写,到处运行。Java 语言编写的源代码在编译...
### JVM学习笔记核心知识点整理 #### 一、引言与背景 随着软件开发技术的不断发展,Java作为一种广泛应用的编程语言,其背后的核心技术——Java虚拟机(JVM)的重要性日益凸显。掌握JVM不仅可以帮助开发者更好地理解...
《JVM必知必会》记录了对JVM的总结及学习笔记,详解的介绍了什么是jvm,以及弄清楚jvm的工作原理等等,会深入了解JVM有一定的帮助。 Java Virtual Machine(Java虚拟机),它bai是一个虚构出来du的计算机,是通过在...
《JVM学习笔记》 Java虚拟机(JVM)是Java平台的核心组成部分,它负责运行所有的Java应用程序。这篇笔记将深入探讨JVM的工作原理、内存管理、类加载机制以及优化策略,帮助读者全面理解JVM并提升Java程序的性能。 ...
这份资料出自B站上的【狂神说Java】系列教程,为快速入门JVM提供了详实的笔记。以下是根据这些资源可能包含的一些关键知识点的详细解析: 1. **JVM概述**: - JVM是Java平台的核心组成部分,它是一个运行Java字节...
这份“JVM的学习笔记PDF版”应该包含了关于JVM的详细信息,帮助学习者深入理解这个复杂的系统。JVM允许Java代码跨平台运行,通过解释器、类加载器、垃圾收集器等组件实现“一次编写,到处运行”的理念。 1. **JVM...
JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。 JVM一直是java知识里面进阶阶段的重要部分,如果希望在java领域研究的更深入,则JVM则是如论如何也避开不了的话题,本系列试图通过...
本文将深入探讨“访问控制器的栈校验机制”,这是JVM安全模型的重要组成部分。 栈校验机制是JVM在执行字节码时进行的一种静态类型检查。当方法调用、字段访问或数组操作等指令被执行时,JVM会检查操作数栈中的数据...
本篇学习笔记将深入探讨这一主题,主要关注Java字节码的执行过程以及如何通过栈校验来防止非法操作。 Java字节码是由Java编译器生成的中间代码,它在JVM上运行前会经过类加载器的验证,其中栈校验是验证的一部分。...
本篇学习笔记将深入探讨这一机制,主要聚焦于步骤2的源码分析。在Java的世界里,类型安全至关重要,尤其是在多线程环境下,防止非法的类型访问和操作能避免潜在的安全风险和程序崩溃。 栈校验机制主要是为了执行...
《JVM内存管理学习笔记》 在Java世界中,JVM(Java Virtual Machine)是运行所有Java应用程序的核心。深入理解JVM内存管理对于优化程序性能、预防和解决内存泄漏问题至关重要。本文将从JVM内存模型、内存区域划分、...
本文将根据"JVM性能学习笔记思维导图"的主题,详细阐述JVM的主要组成部分,性能调优的关键点以及相关的工具与实践策略。** 1. **JVM结构与内存模型** - **类装载器(ClassLoader)**:负责加载类文件,确保类在运行...
本文将深入探讨JVM的内存结构,特别是程序计数器、虚拟机栈、本地方法栈、堆内存以及它们在多线程环境下的工作原理。 程序计数器是JVM内存结构的一部分,它是一个非常小但至关重要的组件。程序计数器用于存储即将...
总结,学习JVM对于深入理解Java应用程序的运行机制至关重要。通过掌握类加载、内存管理、执行引擎的工作原理,以及垃圾回收和性能优化的方法,开发者能更好地编写高效、稳定的Java程序。同时,熟练运用JVM监控工具,...
本学习笔记旨在全面解析JVM的工作原理,涵盖内存管理、类加载机制、垃圾收集、性能调优等多个关键领域,帮助读者从基础到深入地掌握JVM。 1. **JVM结构与运行过程** - JVM由类装载器、运行时数据区、执行引擎、...