- 浏览: 3015971 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
作者:RednaxelaFX
主页:http://rednaxelafx.iteye.com
日期:2009-06-02
系列笔记:
JVM在校验阶段不检查接口的实现状况
CLR上的接口调用也是在运行时检查的
前面两帖我们看到JVM与CLR这两个主流的高级语言虚拟机都对接口方法调用管得很松,不对其被调用对象做静态类型校验。可是为什么呢?
==================================================================
这个问题先放在一边,让我们来看看如果让JVM执行一段字节码,其中调用了不存在的虚方法会怎样。还是用bitescript来生成class文件:
执行这段脚本生成TestVirtualCall.class,执行之,会看到:
也就是说JVM在校验的时候就检查到了错误:要调用的length()是String类上的,而引用是Object类型的,不是String或其派生类,于是被认为校验失败。
JVM与CLR都以支持静态的面向对象类型为主。由于基类上所有实例方法中非私有的方法都自动会被派生类所继承,所以可以保证这些方法在派生类上肯定存在(有没有被覆盖没关系)。持有某类型的引用时,即便实际指向的是这个类的派生类的实例,至少调用该类型上的方法都肯定没问题。所以JVM与CLR对虚方法调用的限制都可以做得比较严格,可以根据引用的类型(而不是实际实例的类型)来做校验。
虽说这种严格性也使得一些本来可以成功的调用在校验时会被拒绝,像这样:
上面这段字节码的最后一条指令在校验的时候会被认为不合法,因为静态校验会认为o的类型是o,与ArrayList.size()要求的被调用对象类型不匹配。但我们通过脑内推导可以看出这里的o实际指向的是ArrayList的实例,这个调用本来应该可以成功的。但JVM不买这帐,宁可认定一些貌似可以貌似不行的状况为不合法,以保证VM的正确性的可证明性。
==================================================================
那调用接口方法的状况呢?
JVM与CLR的类型系统中,接口可以被任何类所实现。基类实现的接口派生也必须实现;基类没有实现的接口,派生类照样可以实现。于是,当我们持有某个类型A的引用时,如果该类型自身并没有实现某接口IB,我们无法确定该引用所指向的对象实例是不是肯定没有实现接口IB:或许类型B继承类型A同时实现了接口IB,而我们所持有的类型A的引用指向的正是类型B的实例。
这种情况无法只用静态检查验证其正确性,但又十分有用,不能一刀切都拒绝掉。所以干脆推迟到运行时通过实际的实例上的类型信息来做检查。
静态校验与动态检查相结合,JVM与CLR等支持静态的面向对象类型系统的虚拟机得以保证其上运行的程序的类型安全。
可以看这样的一个例子,其中IFoo与FooImpl取自前一帖的例子:
这段脚本生成的TestVirtualCall2.class执行正常,打印出"FooImpl.method()"。
与前面调用虚方法的例子对比,可以看到JVM对接口方法调用的静态检查确实松一些。结合前一帖的例子看,这仍然是类型安全的,虽然要推迟到运行时才能检查。
==================================================================
值得注意的是,以上讨论都是以静态类型系统为前提的。如果类的成员能动态的被任意添加删除修改,那接口或基类所定下的契约就不能保证被实现,也就失去了上述讨论的前提。
以前好像看到说,基类的私有方法也会被派生类继承,只是不能调用.
这要看你怎么看待“继承”这个概念了。确实,基类的所有实例成员都会被派生类所继承,包括成员域和成员方法;其中只有成员方法与多态有关,而私有方法不参与多态。如果不能被调用、不参与多态,那么它对外界来说就是不可观察到的,至少不会成为“契约”的一部分。(Java序列化的那俩方法……那是例外)
可以举个例子。在CLR当中,每个类型都与一个方法表的实例联系在一起。方法表当中,继承下来的虚方法的项总是位于方法表中固定偏移量的位置上,紧接着是该类型新增加的虚方法,然后才是私有方法和构造器等。派生类的方法表里不包含基类的私有方法或静态方法的项——既然不能调用,何必记录下来呢?这样的设计在调用虚方法总能使用相对方法表起始位置的固定偏移量,间接调用,而在调用静态方法或私有方法时则使用固定地址,直接调用。
以前好像看到说,基类的私有方法也会被派生类继承,只是不能调用.
主页:http://rednaxelafx.iteye.com
日期:2009-06-02
系列笔记:
JVM在校验阶段不检查接口的实现状况
CLR上的接口调用也是在运行时检查的
前面两帖我们看到JVM与CLR这两个主流的高级语言虚拟机都对接口方法调用管得很松,不对其被调用对象做静态类型校验。可是为什么呢?
==================================================================
这个问题先放在一边,让我们来看看如果让JVM执行一段字节码,其中调用了不存在的虚方法会怎样。还是用bitescript来生成class文件:
require 'rubygems' require 'bitescript' include BiteScript JObject = java.lang.Object JString = java.lang.String fb = FileBuilder.build(__FILE__) do public_class 'TestVirtualCall' do public_static_method 'main', void, string[] do # Object o = new Object(); new JObject dup invokespecial JObject, '<init>', [void] astore 1 # int i = ((String)o).length(); aload 1 ## checkcast JString # without this cast, there will be a invokevirtual JString, 'length', [int] # VerifyError at verification time istore 2 returnvoid end end end fb.generate do |filename, class_builder| File.open(filename, 'w') do |file| file.write(class_builder.generate) end end
执行这段脚本生成TestVirtualCall.class,执行之,会看到:
Exception in thread "main" java.lang.VerifyError: (class: TestVirtualCall, method: main signature: ([Ljava/lang/String;)V) Incompatible object argument for function call Could not find the main class: TestVirtualCall. Program will exit.
也就是说JVM在校验的时候就检查到了错误:要调用的length()是String类上的,而引用是Object类型的,不是String或其派生类,于是被认为校验失败。
JVM与CLR都以支持静态的面向对象类型为主。由于基类上所有实例方法中非私有的方法都自动会被派生类所继承,所以可以保证这些方法在派生类上肯定存在(有没有被覆盖没关系)。持有某类型的引用时,即便实际指向的是这个类的派生类的实例,至少调用该类型上的方法都肯定没问题。所以JVM与CLR对虚方法调用的限制都可以做得比较严格,可以根据引用的类型(而不是实际实例的类型)来做校验。
虽说这种严格性也使得一些本来可以成功的调用在校验时会被拒绝,像这样:
// Object o = (Object)new ArrayList(); new Ljava/util/ArrayList; dup invokespecial java/util/ArrayList."<init>":()V checkcast java/lang/Object // << ensures the JVM think local 1 is of type Object astore 1 // o.size(); // note that o actually points to an instance of ArrayList aload 1 invokevirtual java/util/ArrayList.size:()I
上面这段字节码的最后一条指令在校验的时候会被认为不合法,因为静态校验会认为o的类型是o,与ArrayList.size()要求的被调用对象类型不匹配。但我们通过脑内推导可以看出这里的o实际指向的是ArrayList的实例,这个调用本来应该可以成功的。但JVM不买这帐,宁可认定一些貌似可以貌似不行的状况为不合法,以保证VM的正确性的可证明性。
==================================================================
那调用接口方法的状况呢?
JVM与CLR的类型系统中,接口可以被任何类所实现。基类实现的接口派生也必须实现;基类没有实现的接口,派生类照样可以实现。于是,当我们持有某个类型A的引用时,如果该类型自身并没有实现某接口IB,我们无法确定该引用所指向的对象实例是不是肯定没有实现接口IB:或许类型B继承类型A同时实现了接口IB,而我们所持有的类型A的引用指向的正是类型B的实例。
这种情况无法只用静态检查验证其正确性,但又十分有用,不能一刀切都拒绝掉。所以干脆推迟到运行时通过实际的实例上的类型信息来做检查。
静态校验与动态检查相结合,JVM与CLR等支持静态的面向对象类型系统的虚拟机得以保证其上运行的程序的类型安全。
可以看这样的一个例子,其中IFoo与FooImpl取自前一帖的例子:
require 'rubygems' require 'bitescript' include BiteScript IFoo = Java::IFoo FooImpl = Java::FooImpl fb = FileBuilder.build(__FILE__) do public_class 'TestVirtualCall2' do public_static_method 'main', void, string[] do # Object o = new FooImpl(); new FooImpl dup invokespecial FooImpl, '<init>', [void] checkcast JObject # << make sure the JVM think local 1 is of type Object astore 1 # o.method(); aload 1 invokeinterface IFoo, 'method', [void] # this works! returnvoid end end end fb.generate do |filename, class_builder| File.open(filename, 'w') do |file| file.write(class_builder.generate) end end
这段脚本生成的TestVirtualCall2.class执行正常,打印出"FooImpl.method()"。
与前面调用虚方法的例子对比,可以看到JVM对接口方法调用的静态检查确实松一些。结合前一帖的例子看,这仍然是类型安全的,虽然要推迟到运行时才能检查。
==================================================================
值得注意的是,以上讨论都是以静态类型系统为前提的。如果类的成员能动态的被任意添加删除修改,那接口或基类所定下的契约就不能保证被实现,也就失去了上述讨论的前提。
评论
2 楼
RednaxelaFX
2009-06-05
cescshen 写道
引用
引用基类上所有实例方法中非私有的方法都自动会被派生类所继承
以前好像看到说,基类的私有方法也会被派生类继承,只是不能调用.
这要看你怎么看待“继承”这个概念了。确实,基类的所有实例成员都会被派生类所继承,包括成员域和成员方法;其中只有成员方法与多态有关,而私有方法不参与多态。如果不能被调用、不参与多态,那么它对外界来说就是不可观察到的,至少不会成为“契约”的一部分。(Java序列化的那俩方法……那是例外)
可以举个例子。在CLR当中,每个类型都与一个方法表的实例联系在一起。方法表当中,继承下来的虚方法的项总是位于方法表中固定偏移量的位置上,紧接着是该类型新增加的虚方法,然后才是私有方法和构造器等。派生类的方法表里不包含基类的私有方法或静态方法的项——既然不能调用,何必记录下来呢?这样的设计在调用虚方法总能使用相对方法表起始位置的固定偏移量,间接调用,而在调用静态方法或私有方法时则使用固定地址,直接调用。
1 楼
cescshen
2009-06-05
引用
基类上所有实例方法中非私有的方法都自动会被派生类所继承
以前好像看到说,基类的私有方法也会被派生类继承,只是不能调用.
发表评论
-
The Prehistory of Java, HotSpot and Train
2014-06-02 08:18 0http://cs.gmu.edu/cne/itcore/vi ... -
MSJVM and Sun 1.0.x/1.1.x
2014-05-20 18:50 0当年的survey paper: http://www.sym ... -
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
class data sharing by Apple
2014-03-28 05:17 0class data sharing is implement ... -
HotSpot Server VM与Server Class Machine
2014-02-18 13:21 0HotSpot VM历来有Client VM与Server V ... -
Java 8的lambda表达式在OpenJDK8中的实现
2014-02-04 12:08 0三月份JDK8就要发布首发了,现在JDK8 release c ... -
GC stack map与deopt stack map的异同
2014-01-08 09:56 0两者之间不并存在包含关系。它们有交集,但也各自有特别的地方。 ... -
HotSpot Server Compiler与data-flow analysis
2014-01-07 17:41 0http://en.wikipedia.org/wiki/Da ... -
基于LLVM实现VM的JIT的一些痛点
2014-01-07 17:25 0同事Philip Reames Sanjoy Das http ... -
tailcall notes
2013-12-27 07:42 0http://blogs.msdn.com/b/clrcode ... -
《自制编程语言》的一些笔记
2013-11-24 00:20 0http://kmaebashi.com/programmer ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22253(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局 (0): 拿在手上的是什么
2013-11-04 18:22 21370(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ... -
也谈类型: 数据, 类型, 标签
2013-08-18 01:59 0numeric tower http://en.wikiped ... -
oop、klass、handle的关系
2013-07-30 17:34 0oopDesc及其子类的实例 oop : oopDesc* ... -
Nashorn各种笔记
2013-07-15 17:03 0http://bits.netbeans.org/netbea ...
相关推荐
从JVM内存管理的角度谈谈静态方法和静态属性
垃圾回收系列(3):CLR与JVM垃圾回收器的比较扫描.pdf
下面小编就为大家带来一篇JVM 方法调用之静态分派(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
静态方法为什么不能调用非静态成员? 静态方法和实例方法有何不同? 重载和重写的区别 什么是可变长参数? 基本数据类型 Java 中的几种基本数据类型了解么? 基本类型和包装类型的区别? 包装类型的缓存机制了解么? ...
资源描述: 1.JVM中[本地方法接口]的所有内容-xmind脑图pdf 2.资源内容:JVM中[本地方法接口]的所有内容 3.学习目标:了解jvm底层原理 4.特点:简单易懂,容易上手 5.使用说明:需要使用pdf打开
4.main 方法为什么是静态的?能不能改为非静态? main()方法一定是静态的,如果 main()是非静态的那么在调用 main 方法时 JVM 就得实例化 它的类。 不能改为非静态,main()方法必须声明为静态的,这样 JVM 才可以...
Java中方法的调用对应字节码有5条指令: ...invokeinterface:用于调用接口方法,会在运行时再确定一个实现该接口的对象。 invokedynamic:先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法。
JVM与GC调优课程视频 〖课程介绍〗: JVM与GC调优课程视频 〖课程目录〗: 1.笔记/ ├── 第1篇-字节码篇.png?x-oss-process=style/pnp8 ├── 第2篇-类的加载篇.png?x-oss-process=style/pnp8 ├── 第3篇-运行时...
JVM优化方法
声明类型(注意与实际类型区分)来选取重载方法。选取的过程共分为三个阶段:1. 在不考虑对基本类型自动装拆箱(auto-boxing,auto-unboxing)
visualvm监控jvm及远程jvm监控方法
讲JVM工作原理、垃圾回收讲的非常好的一个ppt
jvm字节码的调用图生成器
jvm源码
PB 代码通过n_pbnitest对象(该对象为C++编译的pbni对象导入到pb)调用Demo.class对象getHelloWorld方法,返回字符串“Hello world!” 功能 C++编译的pbni的dll文件为PBNI.dll,该文件启动java虚拟机(JNI_...
jvm 配置jvm参数 配置jvm参数
详细内容请看https://blog.csdn.net/qq_34950682/article/details/95790190
144、当我重编译我的JSP使用的一个类后,为什么JVM继续使用我的老CLASS? 36 145、与之间的差别? 36 146、JSP的缺点? 36 148、如何实现JSP的国际化? 36 150、如何在JSP中包括绝对路径文件? 使用URLConnection...
46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT...
jvm 详细介绍,了解jvm各个组成部分和功能