深入理解JVM--类的执行机制
在完成将class文件信息加载到JVM并产生class对象之后,就可以执行Class对象的静态方法或者实例方法对对象进行调用了。JVM在源代码编译阶段将源代码编译为字节码文件,字节码是一种中间代码的方式,要由JVM在运行时进行解释执行,这种方式称之为解释执行方式。
1、字节码的解释执行
SunJDK是基于栈的体系结构来执行字节码的,基于栈的好处是代码紧凑,体积小。每个线程创建之后,都会产生一个程序计数器(又称PC计数器)和栈,栈中存在若干的栈帧,每个方法的调用都会产生栈帧。栈帧主要包含两部分,局部变量和操作数栈,局部变量用户存放方法中的局部变量和参数,操作数栈用于存放方法中性过程中产生的中间结果,栈帧中还会有一些杂用的空间,用于存放指向方法已解析的常量池的引用,一些JVM内部所需要的数据等。如下图所示
1)指令解释执行
方法的指令执行是经典的冯诺依曼体系中饿FDX的循环方式,即取一条指令,然后分派,执行。如下面的代码
while(true){int code=fetchNextCode();switch(code){case IADD:// do addcase …:// do sth. }}
以上程序很简单的满足了我们的需求,也是FDX循环模式一个很简单的实现,但是这段程序每次执行完毕都会回到程序的原点,重新判断重新执行,效率是比较差的,所以,有人提出了一种叫token-threding的方式,使用令牌来进行处理,如下面的代码
IADD:{// do add; fetchNextCode();dispatch();}ICONST_0:{//do sth fetchNextCode();dispatch();}
这段代码冗余了fetchNextCode和dispatch两条指令,性能相对会好一些(为什么性能会好?详情请参看博文JAVA类的执行机制)但是使用这种方法由于冗余相关指令,因此占用的内存会大一些。
2) 栈顶缓存
在方法执行过程中,可以看到有很多重要的操作将数据放入到操作数栈,这会导致寄存器和内存要不断的进行交换数据,SunJDK采用可栈顶缓存,即将本来位于操作数栈的值直接缓存在寄存器上,这样对于大部分只需要一个操作数的操作而言,无需将数据放入操作数栈,可直接在寄存器上进行计算,然后放回操作数栈。
3) 部分栈帧共享
当一个方法调用另外一个方法的时候,通常会传入另一个方法的参数存放在操作数栈,SunJDK为此做了一个优化,就是当调用方法时,后一方可以将前一方的操作数栈作为当前方法的局部变量,从而节省数据copy带来的损耗。
当然SunJDK为了提高数据执行的效率,做了很多优化,这里有不一一列举了。
2、编译执行
解释执行的效率是很低的,为了提升代码编译的执行性能,SunJDK也提供了编译执行的机制(N多教科书上说java是一种解释型语言,都是不全面的,JVM是可以执行编译执行的),编译在运行时进行,通常成为JIT编译器。SunJDK对执行频率高的代码进行编译,而对执行频率不高的代码仍然进行解释执行,因此SunJDK又被称作为HotspotVM,对于编译,SunJDK提供了2种方式,clint complier(-clint)和server complier(-server)。
Clint complier又被成为C1,较为轻量级,只做少量的性能开销比较高的一些优化,占用内存较少,适合做桌面的交互式应用。在寄存器分配策略上,JDK6采用了线性扫描寄存器的方式(具体可参照http://www.bluedavy.com/book/reference/LSRA.pdf),在其他方面的优化主要包括:方法内联、去虚拟化、冗余消除等!
1) 方法内联
对于java语言来说,通常需要调用多个方法来完成功能,执行的时候还需要多次传参和返回值传递,因此C1采用了方法内联的方式,把用到的方法直接植入到当前的方法中,示例如下
public void buy(){saveOrder();saveDetail();}public void saveOrder(){//do save saveOrder }public void saveDetail(){//do write detail}
当编译的时候,发现编译后的buy方法的字节码≤35个字节(这个可以通过JVM启动的时候设置参数-XX:MaxInlineSize=35来进行控制),则该程序会演变成下面的结构!
public void buy(){//do save saveOrder//do write detail }
2) 去虚拟化
去虚拟化是指在状态class文件之后进行层次分析,如果发现类中的方法只提供了一个实现类,那么对于条用了此方法的代码,也就可以通过方法内联来提高性能。如下列代码
interface OrderInterface{public void buy(); } class Order implements OrderInterface{@Overridepublic void buy() {//do buy } } public class Test{public void testBuy(Order order){order.buy();}
当JVM在编译的时候发现buy方法只有一个实现的时候,就演变成以下结构
public class Test{public void testBuy(Order order){//do buy } }
3) 冗余消除
冗余消除是指在编译时,根据运行时的状况进行代码折叠或削除,如下代码
import com.sun.org.apache.commons.logging.Log; import com.sun.org.apache.commons.logging.LogFactory; public class Test{private final static Log log = LogFactory.getLog(Test.class);private final static boolean isDebug = log.isDebugEnabled();public void execute(){if(isDebug)log.debug("Test.execute() has been excuted");//do sth } }
这段代码在编译的时候如果被监测到isDebug是false,则执行完C1编译之后就变成了如下代码
public class Test{public void execute(){//do sth } }
这也是很多时候优秀的程序员不直接写log.debug而要先写一个判断的原因。
Server complier又称为C2编译,就比较重量级,这个我们下次再讲。今天先到此吧,休息一下,哈哈!
深入理解JVM--类的执行机制相关推荐
- 深入理解JVM之代码执行机制与线程资源同步及交互机制
Java规范定义标准结构如图3.1 Java代码的执行机制 Java源码编译机制 javac将Java源码编译为class文件的步骤如图3.2 1.分析和输入到符号表(Parse and Enter) ...
- jvm垃圾回收机制_深入理解JVM的垃圾回收机制
如何判断对象已"死" Java堆中存放着几乎所有的对象实例,垃圾回收器在堆进行垃圾回收前,首先要判断这些对象那些还存活,那些已经"死去".判断对象是否已&qu ...
- 【深入理解JVM】ClassLoader类加载机制
Java程序并不是一个原生的可执行文件,而是由许多独立的类文件组成,每一个文件对应一个Java类.此外,这些类文件并非立即全部装入内存的,而是根据程序需要装入内存.ClassLoader专门负责类文件 ...
- spi 动态加载、卸载_理解 ServiceLoader类与SPI机制
对于Java中的Service类和SPI机制的透彻理解,也算是对Java类加载模型的掌握的不错的一个反映. 了解一个不太熟悉的类,那么从使用案例出发,读懂源代码以及代码内部执行逻辑是一个不错的学习方式 ...
- fegin需要实现类_深入理解JVM(六)--虚拟机类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类从被加载到虚拟机内存开始,到卸载出内存为止 ...
- 深入理解JVM(5)——虚拟机类加载机制
在Class文件中描述的各种信息,最终都需要加载到虚拟机中之后才能运行和使用.而虚拟机中,而虚拟机如何加载这些Class文件?Class文件中的信息进入到虚拟机中会发生什么变化?本文将逐步解答这些问题 ...
- 深入理解JVM类文件格式
我们知道Java最有名的宣传口号就是:"一次编写,到处运行(Write Once,Run Anywhere)",而其平台无关性则是依赖于JVM, 所有的java文件都被编译成字节码 ...
- java 执行机制_Java类的执行机制
在完成将class文件信息加载到JVM并产生Class对象后,就可执行Class对象的静态方法或实例化对象进行调用了.在源码编译阶段将源码编译为JVM字节码,JVM字节码是一种中间代码的方式,要由JV ...
- 深入理解JVM之虚拟机类加载机制
1.概述 Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制.与那些在编译时需要进 ...
- vuex 的理解,vuex 的执行机制
vuex 是 vue 的状态管理工具 管理项目中的公共数据 能够在所有的组件中使用 vuex一共有五大核心: state 存放公共数据的地方 通过 this.$store.state.xxx调用 mu ...
最新文章
- 超硬核的 Python 数据可视化教程!
- 面试题整理12 求字符串括号最大深度子串
- linux 命令综述
- HttpClient-01基本概念
- .net 从txt中读取行数据_【VBA项目】从指定文件中读取数据并绘制图表
- oracle+semijoin,Semi join 与anti join
- 洛谷1008 三连击
- 宜普电源转换公司(EPC)于2018年WiPDA宽能隙功率器件及应用论坛与工程师作技术交流
- P1179 数字统计
- 北京迷笛音乐节阵容、北京草莓音乐节阵容
- 悲情陨落的十大民族品牌
- vue项目中页面打印插件(去除页眉页脚)
- 2019年肖秀荣命题人精讲精练
- 【个人向】《春物》 小说原文关键段落摘录
- Driller分析与改进(二)
- 方差、协方差、协方差矩阵以及互相关矩阵
- 4路编码器脉冲计数器,转速测量,8路DO,Modbus TCP数据采集模块
- Centos7安装Mysql(yum 安装)
- 2022-2028全球机载温度传感装置行业调研及趋势分析报告
- 周杰伦《说好不哭》:眼泪流完,青春也就结束了
热门文章
- 创建WPF单实例应用程序
- AJAX,JSON与MVC
- 07-02 测试报告-allure
- android runnable内存泄漏,这个Runnable可以防止内存泄漏吗?
- 使用aws跑深度学习_RNN示例项目从开发到部署(三):在AWS上部署深度学习模型...
- html判断用户名的合法性,javascript简单判断输入内容是否合法的方法
- python最大的社区_python 最大流
- 2020年前端如何适应大环境,发展的前途与趋势是怎么样的?
- cookielifetime php_PHP session有效期session.gc_maxlifetime的设置方法
- ipad导入pdf_Ipad笔记法①日常笔记篇