当前位置: 首页 > news >正文

搜索网站排名软件seo网站诊断价格

搜索网站排名软件,seo网站诊断价格,网络设计课程实验,自己做的网站能联网吗【0】README0.1#xff09;本文转自 “深入理解jvm”#xff0c;旨在学习 虚拟机字节码执行引擎 的基础知识#xff1b;【1】概述1#xff09;物理机和虚拟机的执行引擎#xff1a; 物理机的执行引擎是直接建立在处理器#xff0c;硬件#xff0c;指令集和操作系统层面上…【0】README0.1本文转自 “深入理解jvm”旨在学习 虚拟机字节码执行引擎 的基础知识【1】概述1物理机和虚拟机的执行引擎 物理机的执行引擎是直接建立在处理器硬件指令集和操作系统层面上的而虚拟机的执行引擎则是由自己实现的因此可以自行制定指令集与执行引擎的结构体系并且能够执行那些不被硬件直接支持的指令集格式2在不同的虚拟机实现里面执行引擎在执行java代码的时候可能会有解释执行和编译执行两种选择甚至还可能会包含几个不同级别的编译器执行引擎。3所有的java虚拟机的执行引擎都是一致的输入的是字节码文件处理过程是字节码解析的等效过程输出的是执行结果本章将主要从概念模型的角度来讲解虚拟机的方法调用和字节码执行【2】运行时栈帧结构1栈帧栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构它是虚拟机运行时数据区中的虚拟机栈的栈元素。干货——栈帧定义review——虚拟机栈的作用每个方法在执行的时候都会创建一个栈帧用于存储局部变量表操作数栈动态链接方法出口等信息2当前栈帧和当前方法对于执行引擎来说在活动线程中只有位于栈顶的栈帧才是有效的称为当前栈帧与这个栈帧相关联的方法称为当前方法3执行引擎运行的所有字节码指令都只针对当前栈帧进行操作栈帧结构如下图所示【2.1】局部变量表1局部变量表定义是一组变量值存储空间用于存放方法参数和方法内部定义的局部变量在 java程序编译为Class 文件时就在方法的Code属性的max_locals 数据项中确定了该方法所需要分配的局部变量表的最大容量干货——局部变量表定义2局部变量表容量以变量槽Slot为最小单位虚拟机规范中并没有明确指明一个Slot 应占用的内存空间大小只是很有导向性地说到每个Slot都应该能够存放一个 booleanbytecharshortintfloatreferencereturnAddress 类型的数据这8种数据类型都可以使用32位或更小的物理内存来存放干货——引入最小存储单位——变量槽的概念3一个Slot可以存放一个32位以内的数据类型java中占用32位以内的数据类型有 booleanbytecharshortintfloatreference和returnAddress 8种类型。3.1第7种reference类型表示对一个对象实例的引用虚拟机实现至少都应当能通过这个引用做到两点一是从此引用中直接或间接地查找到对象在java堆中的数据存放的起始地址索引二是此引用中直接或间接地查找到对象所属数据类型在方法区中的存储的类型信息否则无法实现java语言规范中定义的语法约束3.2第8种即returnAddress 类型它是为字节码指令 jsr, jsr_w 和 ret服务的指向了一条字节码指令的地址 4对于64位的数据类型虚拟机会以高位对齐的方式为其分配两个连续 的Slot 空间且64位的数据类型只有long和double两种5虚拟机通过索引定位的方式使用局部变量表索引值范围从0~最大的Slot数量5.1如果访问的是32位数据类型的变量索引n代表了第n个Slot5.2如果访问的是64位数据类型的变量索引n代表了第n个和n1个Slot 6方法执行时虚拟机使用局部变量表完成参数值到参数变量列表的传递过程的局部变量表中第0位索引的Slot默认是用于传递方法所属对象实例的引用在方法中可以通过关键字this来访问到这个隐含的参数干货——通过关键字this来访问到这个隐含的参数7局部变量表中的Slot是可重用的方法体中定义的变量其作用域并不一定会覆盖整个方法体如果当前字节码PC 计数器的值已经超出了某个变量的作用域那这个变量对应的Slot 就可以交给其他变量使用了。干货——Slot的可重用性7.1不过Slot的可重用性会带来一些副作用如会直接影响到系统的垃圾收集行为如下面3个荔枝所示//通知jvm 进行垃圾收集// 虚拟机运行参数中加上 -verbose:gc 来看看垃圾收集过程发现在System.gc()运行后并没有回收这64M 的内存// 没有回收的原因因为在执行System.gc() 时变量placeholder 还处于作用域内虚拟机自然不会回收其内存/span对以上荔枝的分析AnalysisA1 placeholder能否被回收的根本原因是局部变量表中的Slot 是否还存有关于 placeholder 数组对象的引用。第一次修改中SlotReuse2 代码代码虽然已经离开了placeholder的作用域但在此之后没有任何对局部变量表的读写操作placeholder原本所占用的Slot还没有被其他变量所复用所以作为 GC Roots 一部分的局部变量仍然保持着对它的关联干货——变量所占Slot被回收的原因A2推荐手动将变量设置为null值该操作可以作为一种极特殊情形对象占用内存大此方法的栈帧长时间不能被回收方法调用次数达不到JIT的编译条件下的奇技来使用《practical java》中把“不使用对象应手动赋值为null”作为一条推荐的编码规则 8但笔者本书作者的观点是没有必要把置null 当做一个普遍的编码规则来推广。原因有两点干货——作者认为没有必要把置null 当做一个普遍的编码规则来推广8.1原因一从编码角度讲以恰当的变量作用域来控制变量回收时间才是最优雅的解决方法8.2原因二从执行角度讲使用赋null值的操作来优化内存回收是建立在对字节码执行引擎概念模型的理解之上的而赋null值的操作在经过 JIT 编译优化后就会被消除掉这时候将变量设置为null就是没有意义的 9局部变量不存在准备阶段干货——类变量有两次赋初始值过程而局部变量只有一次赋初始值过程9.1类变量有两次赋初始值的过程一次在准备阶段赋予系统初始值另外一次在初始化阶段赋予程序员定义的初始值所以在初始化阶段没有为类变量赋值也没有关系因为它有一个确定的初始值9.2局部变量不一样如果一个局部变量定义了但没有赋初始值是不能使用的不要认为java中任何情况下都存在诸如整型变量默认为0布尔变量默认为false等默认值9.3看个荔枝下面这段代码并不能运行因为局部变量没有赋初始值 【2.2】操作数栈1操作数栈也称为操作栈它是一个后入先出栈。操作数栈的最大深度在编译的时候写入到 Code属性的max_stacks 数据项中2当一个方法刚刚开始执行的时候这个方法的操作数栈是空的在方法的执行过程中会有各种字节码指令往操作数栈中写入和提取内容也就是出栈和入栈操作3两个栈帧之间的数据共享令两个栈帧出现一部分重叠下面的栈帧的部分操作数栈与上面栈帧的部分局部变量表重叠在一起这样在进行方法调用时就可以共用一部分数据无需进行额外的参数复制传递重叠过程如下图所示干货——两个栈帧之间的数据共享【2.3】动态连接1每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用持有这个引用是为了支持方法调用过程中的动态连接2静态解析 这些符号引用一部分会在类加载阶段或第一次使用的时候就转化为直接引用这种转化称为静态解析3动态连接另外一部分将在每一次运行期间转化为直接引用这部分称为动态连接干货——静态解析和动态连接的定义【2.4】方法返回地址1方法执行后有两种方式退出这个方法way1正常完成出口执行引擎遇到任意一个方法返回的字节码指令这种退出方法称为正常完成出口way2异常完成出口在方法执行过程中遇到了异常并且这个异常没有在方法体内得到处理无论是java虚拟机内部产生的异常还是代码中使用athrow 字节码指令产生的异常只要在本方法的异常表中没有搜索到匹配的异常处理器方法就会退出这种方式称为异常完成出口 2方法退出的过程实际上就等同于把当前栈帧出栈因此退出时可能执行的操作有operationsoperation1恢复上层方法的局部变量表和操作数栈operation2把返回值压入调用者栈帧的操作数栈中调整PC计数器的值以指向方法调用指令后面的一条指令等 【2.5】 附加信息1虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧之中如与调试相关的信息2栈帧信息一般会把动态连接方法返回地址与其他附加信息全部归为一类称为栈帧信息【3】方法调用1方法调用不等于方法执行方法调用阶段唯一的任务就是确定被调用方法的版本暂时还不涉及方法内部的具体运行过程干货——方法调用阶段的唯一任务2一切方法调用在Class文件里面存储的都只是符号引用而不是方法在实际运行时内存布局中的入口地址【3.1】解析1所有方法调用的目标方法在Class文件里面都是一个常量池中的符号引用在类加载的解析阶段会将其中一部分符号引用转化为直接引用2解析定义这种解析能成立的前提是方法在程序真正运行前就有一个可确定的调用版本并且这个方法的调用版本在运行期是不可改变的。换句话说调用目标在程序代码写好编译器进行编译时就必须确定下来。这类方法的调用称为解析干货——解析的定义3在java语言中符合“编译期可知运行期不可变”这个要求的方法主要包括静态方法和私有方法两大类前者与类直接相关后者在外部不可被访问这两种方法各自的特点决定了它们都不可能通过继承或别的方式重写其他版本因此它们都适合在类加载阶段进行解析干货——引入编译期可知运行期不可变的概念4与之对应的是在jvm 中提供了5条方法调用字节码指令分别如下干货——5条方法调用字节码指令4.1invokestatic调用静态方法4.2invokeespecial调用实例构造器init方法私有方法和父类方法4.3invokevirtual调用所有的虚方法4.4invokeinterface调用接口方法会在运行时再确定另一个实现此接口的对象4.5invokedynamic先在运行时动态解析出调用点限定符所引用的方法然后再执行该方法在此之前的4条调用指令分派逻辑是固化在java虚拟机内部 的而invokedynamic 指令的分派逻辑是由用户所设定的引导方法决定的 5非虚方法与虚方法干货——非虚方法与虚方法定义5.1非虚方法只要能被invokestatic 和 invokespecial 指令调用的方法都可以在解析阶段中确定唯一的调用版本符合这个条件的有静态方法私有方法实例构造器父类方法4类他们在类加载的时候就会把符号引用解析为对该方法的直接引用。这些方法称为非虚方法5.2虚方法其他的方法称为虚方法除去final方法 6看个荔枝静态方法sayHello只可能属于类型 StaticResolution没有任何手段可以覆盖或隐藏这个方法7java中的非虚方法补充被final修饰的方法final方法无法被覆盖没有其他版本所以也无需对方法接收者进行多态选择又或者说多态选择的结果肯定是唯一的8解析调用一定是个静态的过程在编译期间就完全确定在类装载的解析阶段就会把涉及的符号引用全部转变为 可确定的直接引用不会延迟到运行期再去完成9分派调用可能是静态的也可能是动态的根据分配依据的宗量数可分为单分派和多分派。这两类分派方式的两两组合就构成了静态单分派静态多分派动态单分派动态多分派4种分派组合情况干货——4种分派调用方式【3.2】分派1静态分派1.1看个荔枝public class StaticDispatch {static abstract class Human{}static class Man extends Human{}static class Woman extends Human{}public void sayHello(Human guy) {System.out.println(hello, guy!);}public void sayHello(Man guy) {System.out.println(hello, gentleman!);}public void sayHello(Woman guy) {System.out.println(hello, lady!);}public static void main(String[] args) {Human man new Man();Human woman new Woman();StaticDispatch sd new StaticDispatch();sd.sayHello(man);sd.sayHello(woman);}/*** output:* hello, guy!* hello, guy!*/ }1.2静态类型实际类型上面代码中的Human 称为变量的静态类型或叫做外观类型后面的Man 则称为变量的实际类型干货——外观类型和实际类型定义 1.3静态类型和实际类型的区别静态类型的变化仅仅在使用时发生变量本身的静态类型不会被改变并且最终的静态类型是在编译器可知的而实际类型变化的结果在运行期才可确定编译器在编译程序的时候并不知道一个对象的实际类型是什么。1.4静态分派定义所有依赖静态类型来定位方法执行版本的分派动作称为静态分派静态分派的典型应用是方法重载静态分派发生在编译阶段因此确定静态分派的动作实际上不是由虚拟机来执行的干货——静态分派的典型应用是方法重载1.5看个荔枝干货荔枝// 一个方法重载的荔枝 public class Overload {public static void sayHello(Object arg) {System.out.println(hello object);}public static void sayHello(int arg) {System.out.println(hello int);}public static void sayHello(long arg) {System.out.println(hello long);}public static void sayHello(Character arg) {System.out.println(hello Character);}public static void sayHello(char arg) {System.out.println(hello char);}public static void sayHello(char ...arg) {System.out.println(hello char...);}public static void sayHello(Serializable arg) {System.out.println(hello serializable);}public static void main(String[] args) {sayHello(a);// output: hello char} } case0output为 hello char case1若注释掉sayHello(char arg)output为 hello int这时发生了一次自动类型转换a除了可以代表一个字符串还可以代表数字97因此参数类型为int 的重载也是合适的case2若注释掉sayHello(int arg)output为 hello long这时发生了两次自动类型转换a 转型为整数97之后进一步转换为为长整数97L匹配参数类型为long的重载case3实际上自动转型还能继续发生多次按照char-int-long-float-double 的顺序转型进行匹配。但不会匹配到byte和short类型的重载因为char 到 byte 或short 的转型是不安全的。case4若注释掉sayHello(long arg)output为 hello Character这时发生了一次自动装箱a被包装为它的封装类型java.lang.Character所以匹配到参数类型为 Character的重载case5若注释掉sayHello(Character arg)output为 hello Serializable 因为java.lang.Serializable 是 java.lang.Character类实现的一个接口当自动装箱之后还是找不到装箱类但是找到了装箱类实现了的接口类型所以紧接着又发生了一次自动转型。char 可以转型为int但Character是绝对不会转型为Integer的它只能安全地转型为它实现的接口或父类 case5.1Character还实现了另外一个接口 java.lang.ComparableCharacter 如果同时出现两个参数分别为 Serializable 和 ComparableCharacter 的重载方法那它们在此时的优先级是一样的。编译器无法确定要自动转型为哪种类型会提示类型模糊拒绝编译编译时需要显式指定字面量的静态类型如sayHello(ComparableCharactera)才能编译通过  case6若注释掉sayHello(Serializable arg)output为 hello objectcase7若注释掉sayHello(Object arg)output为 hello char... ConclusionC17个重载方法已经被注释得只剩一个了可见变长参数的重载优先级是最低的这时候字符a 被当做了一个数组元素C2笔者讲述的解析与分派这两者之间的关系并不是二选一的排他关系他们是在不同层次上去筛选确定目标方法的过程。例如前面说过静态方法会在类加载器就进行解析而静态方法显然也是可以拥有重载版本的选择重载版本的过程也是通过静态分派完成 的干货——请注意解析和分派的区别解析是和方法重载相关同方法名不同方法参数列表而分派是与方法重写相关子类父类的方法重写确定调用方法的super version 还是 sub version 2动态分派干货——动态分派的与方法重写有密切关联2.1动态分派和重写有着很密切的关联2.2看个荔枝// 方法动态分派演示 public class DynamicDispatch {static abstract class Human {protected abstract void sayHello();}static class Man extends Human {Overrideprotected void sayHello() {System.out.println(man say hello!);}}static class Woman extends Human {Overrideprotected void sayHello() {System.out.println(woman say hello!);}}public static void main(String[] args) {Human man new Man();Human woman new Woman();man.sayHello(); // man say hello!woman.sayHello(); // woman say hello!man new Woman();man.sayHello(); // woman say hello!} } 对以上输出结果的分析AnalysisA1java虚拟机是如何根据实际类型来分派方法执行版本的呢A2invokevirtual指令的运行时解析过程大致分为以下几个steps step1找到操作数栈顶的第一个元素所指向的对象的实际类型记做Cstep2如果在类型C中找到与常量中的描述符和简单名称都相符的方法则进行访问权限校验如果通过则返回这个方法的直接引用查找过程结束如果不通过则返回 java.lang.IllegalAccessError 异常step3否则按照继承关系及从下往上依次对C 的各个父类进行第2步的搜索和验证过程step4如果始终没有找到合适的方法则抛出 java.lang.AbstractMethodError异常 2.3方法重写的本质由于 invokevirtual指令执行的第一步就是 在运行期确定接收者的实际类型所以两次调用中的invokevirtual 指令把常量池中的类方法符号引用解析到了不同的直接引用上这个过程就是 java语言中方法重写的本质干货——方法重写的本质2.4动态分派定义我们把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派3单分派和多分派3.1宗量方法的接收者与方法的参数统称为方法的宗量3.2单分派和多分派根据分派基于多少种宗量可以将分派划分为单分派和多分派两种。单分派是根据一个宗量对目标方法进行选择多分派是根据多于一个宗量对目标方法进行选择3.3看个荔枝// 单分派和多分派演示 public class Dispatch {static class QQ {}static class _360 {}public static class Father {public void hardChoice(QQ arg) {System.out.println(father choose qq);}public void hardChoice(_360 arg) {System.out.println(father choose 360);}}public static class Son extends Father {public void hardChoice(QQ arg) {System.out.println(son choose qq);}public void hardChoice(_360 arg) {System.out.println(son choose 360);}}public static void main(String[] args) {Father father new Father();Father son new Son();father.hardChoice(new _360()); // father choose 360son.hardChoice(new QQ()); // son choose qq} } 对以上代码的分析Analysis看看编译阶段编译器的选择过程也就是静态分派过程A1选择目标方法的依据有两点一是静态类型是Father还是Son二是方法参数是QQ 还是360A2这次选择结果的最终产物是产生两条invokevirtual指令两条指令的参数分别是常量池中指向Father.hardChoice(360)以及Father.hardChoice(QQ) 方法的符号引用。因为是根据两个宗量进行选择所以java语言的静态分派属于多分派类型A3动态分派过程在执行“son.hardChoice(new QQ())” 这句代码时更正确地说是在执行这句代码所对应的invokevirtual指令时由于编译期已经决定目标方法的签名必须是 hardChoice(QQ)唯一可以影响虚拟机选择的因素只有此方法的接收者的实际类型是Father还是Son。因为只有一个宗量作为选择依据所以java语言的动态分派属于单分派类型 ConclusionC1所以java语言是一门静态多分派动态单分派的语言干货——java语言是一门静态多分派动态单分派的语言C2按照目前java语言的发展趋势它并没有直接变为动态语言的迹象而是通过内置动态语言如JavaScript执行引擎的方式来满足动态性的需求 4虚拟机动态分派的实现4.1由于动态分派是非常频繁的动作而且动态分派的方法版本选择过程需要运行时在类的方法元数据中搜索合适的目标方法因此在虚拟机的实际实现中基于性能的考虑大部分实现都不会真正地进行如此频繁的搜索。4.2面对这种情况最常用的稳定优化手段就是为类在方法区中建立一个虚方法表使用虚方法表索引来代替元数据查找以提高性能补充-Complementary虚拟机除了使用方法表之外在条件允许的情况下还会使用内联缓存Inline Cache和基于类型继承关系分析(Class Hierarchy Analysis, CHA)技术的守护内联(Guarded Inlining)两种非稳定的激进优化手段来获得更高的性能【3.3】动态类型语言支持invokedynamic指令0invokedynamic指令随着jdk7 的发布字节码指令集终于迎来了第一位新成员——invokedynamic指令。这条新增加的指令是 JDK7 实现“动态类型语言”支持而进行的改进之一也是为JDK8 可以顺利实现 Lambda表达式做的准备干货——引入 invokedynamic指令1动态类型语言1.1定义其关键特征是它的类型检查的主体过程是在运行期而不是编译期满足这个特征的语言有很多包括APLGroovy JS PHP Python Ruby等。相对的在编译期就进行类型检查的语言如C或java就是最常用的静态类型语言干货——引入了动态类型语言静态类型语言1.2看个荔枝对以上代码的分析Analysis A1java虚拟机规范中规定 java.lang.NegativeArraySizeException是一个运行时异常A2运行时异常和连接时异常运行时异常就是只要代码不运行到这一行就不会有问题而连接时异常是指 即使会导致连接时异常的代码放在一条无法执行到的分支路径上类加载时java的连接过程不再编译阶段而在类加载阶段也照样会抛出异常干货——运行时异常和连接时异常的经典描述 1.3在看个荔枝1.4再看荔枝1.4.1java中的 printlnpublic static void main(String[] args) throws FileNotFoundException {PrintStream obj System.out;obj.println(hello world); // output: hello world}对以上代码的分析AnalysisA1对于静态类型语言如javaobj的实际类型必须是PrintStream的子类才是合法的A2但是相同的代码在动态类型语言如JavaScript中情况则不一样无论obj 具体是何种类型只要这种类型的定义中确实包含有 println(String) 方法那方法调用便可成功A3这种差别的原因java等静态类型语言在编译期间已经将 println(String) 方法完整的符号引用生成出来作为方法调用指令的参数存储到 Class文件中如下面这段代码invokevirtual #4; // Method java/io/PrintStream.println:(Ljava/lang/String;)V 这个符号引用包含了此方法定义在哪个具体类型中 方法的名字和参数顺序等信息通过这个符号引用虚拟机可以翻译出这个方法的直接引用。而在JavaScript等动态类型语言中变量obj 本身是没有类型的变量obj的值才具有类型这也是动态类型语言的重要特征干货——动态类型语言如JavaScript的重要特征是变量obj 本身是没有类型的变量obj的值才具有类型 2jdk1.7 与动态类型2.1jdk1.7 中 invokedynamic指令以及 java.lang.invoke 包出现的技术背景 jdk1.7以前的字节码指令集中4条方法调用指令的第一个参数都是被调用的方法的符号引用前面已经提到过方法的符号引用在编译时产生而动态类型语言只有在运行期才能确定接收者类型所以在java虚拟机上实现的动态类型语言就不得不使用其他方式来实现。这个问题终归是应当在虚拟机层次上去解决才最合适因此在java虚拟机层面上提供动态类型的直接支持就称为了java平台的发展趋势之一这是jdk1.7 中 invokedynamic指令以及 java.lang.invoke 包出现的技术背景3java.lang.invoke包3.1jdk1.7新加入的包这个包的主要目的在之前单纯依靠符号引用来确定调用的目标方法这种方式以外提供了一种新的动态确定目标方法的机制称为MethodHandle干货——引入动态确定目标方法的机制——MethodHandle3.2看个荔枝3.2.1如我们用C或C实现一个带谓词的排序函数代码如下void sort(int list[], const int size, int(*compare)(int,int));3.2.2如我们用java实现一个排序函数需要传入Comparatorc接口参数代码如下void sort(List list, Comparator c);3.2.3下面的代码演示了 MethodHandle的基本用途 无论obj是何种类型都可以正确地调用到 println() 方法// MethodHandle 基础用法演示 public class MethodHandleTest {static class ClassA {public void println(String s) {System.out.println(s);}}public static void main(String[] args) throws Throwable {Object obj System.currentTimeMillis() % 2 0 ? System.out : new ClassA();// 无论obj最终是哪个实现类下面这句都能正确调用到 println 方法getPrintlnMH(obj).invokeExact(xiao tangtang); // output:xiao tangtang }private static MethodHandle getPrintlnMH(Object receiver) throws Throwable { // MethodType: 代表方法类型包含方法的返回值和具体参数MethodType mt MethodType.methodType(void.class, String.class);/*** lookup 方法的作用是在指定类中查找符合给定的方法名称方法类型并且符合调用权限的方法句柄* 因为这里调用的是一个虚方法按照java的规则方法第一个参数是隐式的代表该方法的接收者也即是this指向的对象* 这个参数以前是放在参数列表中进行传递的而现在提供了bindTo 方法来完成这件事情*/return MethodHandles.lookup().findVirtual(receiver.getClass(), println, mt).bindTo(receiver);} } 3.3MethodHandle的使用方法和效果与反射Reflection有众多相似之处它们还是有一些差别distinctness干货——MethodHandle与 Reflection的 差别d1本质上讲Reflection和 MethodHandle机制都是在模拟方法调用但Reflection是在模拟 java代码层次的方法调用而MethodHandle 是在模拟字节码层次的方法调用MethodHandles.lookup中的3个方法——findStatic(), findVirtual(), findSpecial() 正是为了对应于 invokestatic, invokevirtual, invokespecial 这几条字节码指令的执行权限校验行为而这些底层细节在使用 Reflection API 时是不需要关心的d2Reflection中的 java.lang.reflect.Method 对象远比 MethodHandle 机制中的 java.lang.invoke.MethodHandle 对象所包含的信息多。前者是方法在java一端的全面映像包含了方法签名描述符以及方法属性表中各种属性的java端表示方式还包含执行权限等的运行期信息而后者仅仅包含与执行该方法相关的信息。更直接一点Reflection是重量级而 MethodHandle 是轻量级的干货——Reflection是重量级而 MethodHandle 是轻量级的d3由于 MethodHandle是对字节码的方法指令调用的模拟所以理论上虚拟机在这方面做的各种优化如方法内联在MethodHandle 上也应当可以采用类似思路去支持。而通过反射去调用方法则不行d4最关键的区别Reflection API 的设计目标是只为 java语言服务的而 MethodHandle 则设计成可服务于所有java虚拟机之上的语言其中也包括 java语言 4invokedynamic 指令干货——intro toinvokedynamic 指令4.1定义jdk1.7 为了更好地支持动态类型语言引入了第5条方法调用的字节码指令 invokedynamic指令4.2invokedynamic指令与 MethodHandle机制的作用是一样的都是为了解决原有4条 invoke* 指令方法分派规则固化在虚拟机之中的问题把如何查找目标方法的决定权从虚拟机转嫁到具体用户代码中4.3动态调用点4.3.1定义每一处含有 invokedynamic指令的位置都称作“动态调用点”这条指令的第一个参数不再是代表方法符号引用的 CONSTANT_Methodref_info 常量而是 CONSTANT_InvokeDynamic_info 常量从这个常量中得到3项信息引导方法 方法类型和名称4.3.2根据 CONSTANT_InvokeDynamic_info 常量中提供的信息虚拟机 可以找到并且执行引导方法从而获得一个Callsite对象最终调用要执行的目标方法看个荔枝 5掌控方法分派规则5.1定义invokedynamic指令与前面4条“invoke*”指令的最大差别就是 它的分配逻辑不是由虚拟机决定的而是由程序员决定的。干货——invokedynamic指令与前面4条“invoke*”指令的最大差别5.2problemsolution  problem如何在类Son 中调用 祖类GrandFather 中的方法呢solution通过MethodType 和 MethodHandle 来解决public class MethodInvokeTest {class GrandFather { // 祖先类void thinking() {System.out.println(i am grandfather);}}class Father extends GrandFather { // 父类void thinking() {System.out.println(i am father);}}class Son extends Father { // 儿子类void thinking() {// 这里如何调用 GrandFather.thinking 方法呢}}class SonReSolution extends Father {void thinking() {try {MethodType type MethodType.methodType(void.class);MethodHandle handle MethodHandles.lookup().findSpecial(GrandFather.class, thinking, type, getClass());handle.invoke(this);} catch (Exception e) {e.printStackTrace();} catch (Throwable e) {e.printStackTrace();}}}public static void main(String[] args) {SonReSolution son new MethodInvokeTest().new SonReSolution();son.thinking();} } 【4】基于栈的字节码解释执行引擎【4.1】解释执行1大部分的程序代码到物理机的目标代码或虚拟机能执行的指令集之前都需要经过如下steps2对于一门具体语言的实现来说2.1这类代表是C/C语言词法分析语法分析以及后面的优化器和目标代码生成器都可以选择独立于执行引擎形成一个完整意义的编译器去实现这类代表是C/C语言2.2这类代表是java也可以把其中一部分实现为一个半独立的编译器 这类代表是java语言3在java语言中javac 编译器完成了程序代码经过词法分析语法分析到抽象语法树在遍历语法树生成线性的字节码指令流的过程。因为这一部分动作是在java虚拟机之外进行的而解释器是在虚拟机的内部所以java程序的 编译就是半独立的实现【4.2】 基于栈的指令集与基于寄存器的指令集1基于栈的指令集这些指令集中的指令大部分都是零地址指令依赖操作数栈进行工作2基于寄存器的指令集这些指令集依赖于寄存器进行工作3基于栈的指令集和基于寄存器的指令集的区别distinctness干货——分别用基于栈的指令集和基于寄存器的指令集计算113.1看个荔枝计算113.1.1基于栈的指令集会是这样 iconst_1 // iconst_1 // 两条 iconst_1 指令连续把两个常量1 压入栈后 iadd // 吧栈顶的两个值出栈相加然后吧结果放回到栈顶 istore_0 // 最后 istore_0 把栈顶的值放到局部变量表的第0个Slot 中/span 3.1.2基于寄存器的程序会是这样 mov eax, 1 add eax, 1/span 【4.3】基于栈的解释器执行过程1看个荔枝对上图的分析AnalysisA0javap 提示这段代码需要深度为2的操作数栈和4个Slot 的局部变量空间A1执行偏移地址为0的指令的情况bipush指令的作用是将单字节的整型常量值-128~127推入操作数栈顶这里推送100 A2执行偏移地址为2的指令的情况istore_1指令的作用是将操作数栈顶的整型值出栈并存放到第1个局部变量Slot中。后续4条指令都是做一样的事情也就是在对应代码中把变量a, b, c 赋值为 100 200 300 A3执行偏移地址为11的指令的情况iload_1 指令的作用是 将局部变量表第1个Slot 中的整型值复制到操作数栈顶 A4执行偏移地址为12的指令的情况iload_2指令的执行过程与 iload_1 类似把第2个Slot的整型值入栈。 A5执行偏移地址为13的指令的情况iadd指令的作用是将操作数栈中头两个栈顶元素出栈做整型加法然后把结果重新入栈。在iadd 指令执行完毕后栈中原有的100和200 出栈他们的和300 重新入栈 A6执行偏移地址为14的指令的情况iload_3指令把存放在第3个局部变量Slot中的300 压入操作数栈中。这是操作数栈为两个整数300.下一条指令 imul 是将操作数栈中头两个栈顶元素出栈做整型乘法然后把结果重新入栈与iadd完全类似 A7执行偏移地址为16的指令的情况ireturn指令是方法返回指令之一它将结束方法执行并将操作数栈顶的整型值返回给此方法的调用者。
http://www.zqtcl.cn/news/115668/

相关文章:

  • 创造网站的最简单 软件是哪个免费全自动推广平台
  • 如何看网站做的好坏vs2017做网站
  • 电子商务网站开发费用入账wordpress商城主题模板下载
  • 广西南宁公司网站制作百度推广自己做网站吗
  • 网站建设公司外链怎么做网站开发职业类别代码
  • 网站优化公司怎么选免费手机网站建设
  • 怎么建立自己的网站平台多少钱专用于做网站公司
  • 怎么修改网站后台权限商城网站制作 价格
  • 英铭广州网站建设wordpress服务器域名
  • 怎么做微商网站怎么生成网站源代码
  • 建设网站怎么设置网站页面大小外贸原单童装哪个网站做
  • 网站布局设计软件太原专业做网站
  • 织梦教育培训网站源码素材图下载
  • 内容网站外贸网站外贸网站建设行吗
  • 什么是网络营销定义北京网站关键词优化
  • 开奖视频网站开发成都优化官网公司
  • 网站开发培训学校互联网软件外包平台
  • 房屋网签查询系统官方网站建设网站总经理讲话范本
  • 创建网站好的平台罗湖网站建设优化
  • 青海兴远建设工程有限公司网站wordpress怎么设计网站
  • 泉州建站公司模板马云谈2025的房价
  • 动漫制作专业什么电脑最适合沈阳关键词优化报价
  • seo企业网站源码虚拟主机如何建设多个网站
  • 电商 网站模板借钱软件推广微信hyhyk1
  • 免费网站模板psd建网站程序工具
  • 企业建设网站专业服务网站设置文件夹权限
  • 用ip做网站威海市城乡建设局网站
  • 网页网站开发设计工作前景做网站 兼职
  • c 网站开发类似优酷乐山旅游英文网站建设
  • 网站空间租用哪家好小程序免费制作平台企业中心