- 浏览: 3013740 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (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
系列笔记:
一个通不过Java字节码校验的例子
数组协变带来的静态类型漏洞
以Python为例讨论高级编程语言程序的wire format与校验
CLR上的接口调用也是在运行时检查的
为什么JVM与CLR都不对接口方法调用做静态校验?
继续看到底要运行一个Java程序需要做的各种检查是在什么时候发生的。这次我们来看看接口调用的问题。
当前的JVM规范中,与方法调用相关的指令有4个:invokevirtual、invokeinterface、invokestatic与invokespecial。其中调用接口方法时使用的JVM指令是invokeinterface。这个指令与另外3个方法调用指令有一个显著的差异:它不要求JVM的校验器(verifier)检查被调用对象(receiver)的类型;另外3个方法调用指令都要求校验被调用对象。也就是说,使用invokeinterface时如果被调用对象没有实现指定的接口,则应该在运行时而不是链接时抛出异常;而另外3个方法调用指令都要求在链接时抛出异常。
看看JVM规范是怎么说的:
可以留意一下另外3个方法调用指令中“IncompatibleClassChangeError”都是Linking Exception而不是Runtime Exception。
这种规定对Java程序来说可见的行为就是:如果一个方法通不过校验,则整个方法都不会被执行;如果能通过校验而抛出运行时异常,则方法当中抛出异常之前的部分都会被执行。
当然,我们直接用Java语言写出来的程序很难引发这样的错误,因为Java编译器会做检查来保证一定程度的类型安全。但是Java的class文件,或者说Java字节码可以由Java编译器以外的别的方式生成,此时就得不到Java编译器对类型安全的保证,而要依赖于JVM对字节码的校验以及运行时的检查了。
================================================================
我是之前在读John Rose对JSR 292的invokedynamic的讲解时留意到invokeinterface的这个特点的。John特别提到invokedynamic就像invokeinterface一样,都不在校验时对被调用对象的类型做检查。不过之前一直没见过调用对一个没实现接口的对象调用接口方法实际是个什么样子。
好吧,这次就来看个例子。首先创建一个接口IFoo,一个实现了该接口的类FooImpl,和一个未实现该接口的类Bar:
IFoo.java:
FooImpl.java:
Bar.java:
接下来构造出一个能引发运行时异常的程序。大致的意思是这样的:
注意第7行代码。如果就这么写然后编译的话,生成的字节码里会有一个checkcast指令将Bar类型的引用转换为IFoo类型的引用。如果有checkcast的话,运行时就会在该指令上报错,因为Bar没有实现IFoo。但这次我想引发的错误不是强制转换相关,而是接口调用相关:想达到的效果是以b为被调用对象,但调用IFoo.method()而不是Bar上已有的方法。所以要靠自己来生成字节码,避免checkcast指令。
上个月的两个相关帖里我使用了ObjectWeb的ASM库来生成Java字节码。这个库很实用,但写起来还是繁琐了些。这次我决定用Charles Nutter写的bitescript。使用该库需要JRuby 1.2.0或更高的版本,我这次用的是JRuby 1.3.0RC2。
安装bitescript只要用JRuby的gem就行:
然后编写生成字节码用的脚本:
test.rb:
可以对比一下直接用ASM时的代码,显然用bitescript要简洁易懂得多。Good job, Charles!
把前面的IFoo.class、FooImpl.class和Bar.class放在“当前目录”下,然后执行上述脚本,生成TestInterfaceCall.class。(注意,执行该脚本时,JRuby要能够找到前面几个.class文件,不然生成出来的代码有错误。留意底下的回复。)
接着,运行java TestInterfaceCall,
可以看到程序打印出了"FooImpl.method()"这句话,也就是说异常是在运行时而不是链接时抛出的。
如今用到Java的字节码改写/动态生成的工具已经很普遍了,如果在使用它们的时候不够小心,相信这里所提到的运行时异常也会有机会见到的 =v=
P.S. 我这次运行的环境是:
=========================
更新:发现这么一篇论文:Using abstract interpretation to add type checking for interfaces in Java bytecode verification
奇怪……请问你运行的脚本是跟我的完全一样还是修改过?完全一样的话,生成的TestInterfaceCall.class也应该是一样的才对。但我这边生成出来的是这样的:
要注意我的脚本里,开头的Java::IFoo等的几行都是没有引号的,或许你的脚本里不小心加了引号?我只是随便猜猜,呵呵。另外你的IFoo写成/Ifoo了。
主页:http://rednaxelafx.iteye.com
日期:2009-06-02
系列笔记:
一个通不过Java字节码校验的例子
数组协变带来的静态类型漏洞
以Python为例讨论高级编程语言程序的wire format与校验
CLR上的接口调用也是在运行时检查的
为什么JVM与CLR都不对接口方法调用做静态校验?
继续看到底要运行一个Java程序需要做的各种检查是在什么时候发生的。这次我们来看看接口调用的问题。
当前的JVM规范中,与方法调用相关的指令有4个:invokevirtual、invokeinterface、invokestatic与invokespecial。其中调用接口方法时使用的JVM指令是invokeinterface。这个指令与另外3个方法调用指令有一个显著的差异:它不要求JVM的校验器(verifier)检查被调用对象(receiver)的类型;另外3个方法调用指令都要求校验被调用对象。也就是说,使用invokeinterface时如果被调用对象没有实现指定的接口,则应该在运行时而不是链接时抛出异常;而另外3个方法调用指令都要求在链接时抛出异常。
看看JVM规范是怎么说的:
Java Virtual Machine Specification, 2nd Edition 写道
invokeinterface
...
Runtime Exceptions
...
if the class of objectref does not implement the resolved interface, invokeinterface throws an IncompatibleClassChangeError.
...
Runtime Exceptions
...
if the class of objectref does not implement the resolved interface, invokeinterface throws an IncompatibleClassChangeError.
可以留意一下另外3个方法调用指令中“IncompatibleClassChangeError”都是Linking Exception而不是Runtime Exception。
这种规定对Java程序来说可见的行为就是:如果一个方法通不过校验,则整个方法都不会被执行;如果能通过校验而抛出运行时异常,则方法当中抛出异常之前的部分都会被执行。
当然,我们直接用Java语言写出来的程序很难引发这样的错误,因为Java编译器会做检查来保证一定程度的类型安全。但是Java的class文件,或者说Java字节码可以由Java编译器以外的别的方式生成,此时就得不到Java编译器对类型安全的保证,而要依赖于JVM对字节码的校验以及运行时的检查了。
================================================================
我是之前在读John Rose对JSR 292的invokedynamic的讲解时留意到invokeinterface的这个特点的。John特别提到invokedynamic就像invokeinterface一样,都不在校验时对被调用对象的类型做检查。不过之前一直没见过调用对一个没实现接口的对象调用接口方法实际是个什么样子。
好吧,这次就来看个例子。首先创建一个接口IFoo,一个实现了该接口的类FooImpl,和一个未实现该接口的类Bar:
IFoo.java:
public interface IFoo { void method(); }
FooImpl.java:
public class FooImpl implements IFoo { public void method() { System.out.println("FooImpl.method()"); } }
Bar.java:
public class Bar { public void anotherMethod() { System.out.println("Bar.anotherMethod()"); } }
接下来构造出一个能引发运行时异常的程序。大致的意思是这样的:
public class TestInterfaceCall { public static void main(String[] args) { IFoo f = new FooImpl(); f.method(); Bar b = new Bar(); ((IFoo)b).method(); // << watch this } }
注意第7行代码。如果就这么写然后编译的话,生成的字节码里会有一个checkcast指令将Bar类型的引用转换为IFoo类型的引用。如果有checkcast的话,运行时就会在该指令上报错,因为Bar没有实现IFoo。但这次我想引发的错误不是强制转换相关,而是接口调用相关:想达到的效果是以b为被调用对象,但调用IFoo.method()而不是Bar上已有的方法。所以要靠自己来生成字节码,避免checkcast指令。
上个月的两个相关帖里我使用了ObjectWeb的ASM库来生成Java字节码。这个库很实用,但写起来还是繁琐了些。这次我决定用Charles Nutter写的bitescript。使用该库需要JRuby 1.2.0或更高的版本,我这次用的是JRuby 1.3.0RC2。
安装bitescript只要用JRuby的gem就行:
gem install bitescript
然后编写生成字节码用的脚本:
test.rb:
require 'rubygems' require 'bitescript' include BiteScript IFoo = Java::IFoo FooImpl = Java::FooImpl Bar = Java::Bar fb = FileBuilder.build(__FILE__) do public_class 'TestInterfaceCall' do public_static_method 'main', void, string[] do # IFoo f = new FooImpl(); new FooImpl dup invokespecial FooImpl, '<init>', [void] astore 1 # f.method(); aload 1 invokeinterface IFoo, 'method', [void] # Bar b = new Bar(); new Bar dup invokespecial Bar, '<init>', [void] astore 2 # ((IFoo)b).method(); aload 2 ## checkcast IFoo # skip the cast to trigger IncompatibleClassChangeError invokeinterface IFoo, 'method', [void] returnvoid end end end fb.generate do |filename, class_builder| File.open(filename, 'w') do |file| file.write(class_builder.generate) end end
可以对比一下直接用ASM时的代码,显然用bitescript要简洁易懂得多。Good job, Charles!
把前面的IFoo.class、FooImpl.class和Bar.class放在“当前目录”下,然后执行上述脚本,生成TestInterfaceCall.class。(注意,执行该脚本时,JRuby要能够找到前面几个.class文件,不然生成出来的代码有错误。留意底下的回复。)
接着,运行java TestInterfaceCall,
D:\sdk\jruby-1.3.0RC2\test_bitescript>java TestInterfaceCall FooImpl.method() Exception in thread "main" java.lang.IncompatibleClassChangeError at TestInterfaceCall.main(test.rb)
可以看到程序打印出了"FooImpl.method()"这句话,也就是说异常是在运行时而不是链接时抛出的。
如今用到Java的字节码改写/动态生成的工具已经很普遍了,如果在使用它们的时候不够小心,相信这里所提到的运行时异常也会有机会见到的 =v=
P.S. 我这次运行的环境是:
D:\sdk\jruby-1.3.0RC2\test_bitescript>java -version java version "1.6.0_11" Java(TM) SE Runtime Environment (build 1.6.0_11-b03) Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode, sharing)
=========================
更新:发现这么一篇论文:Using abstract interpretation to add type checking for interfaces in Java bytecode verification
abstract 写道
Java interface types support multiple inheritance. Because of this, the standard bytecode verifier ignores them, since it is not able to model the class hierarchy as a lattice. Thus, type checks on interfaces are performed at run time. We propose a verification methodology that removes the need for run-time checks. The methodology consists of: (1) an augmented verifier that is very similar to the standard one, but is also able to check for interface types in most cases; (2) for all other cases, a set of additional simpler verifiers, each one specialized for a single interface type. We obtain these verifiers in a systematic way by using abstract interpretation techniques. Finally, we describe an implementation of the methodology and evaluate it on a large set of benchmarks.
评论
5 楼
Arbow
2009-06-08
果然如此,需要用 jruby -J-cp ./ ./test.rb 运行
4 楼
Arbow
2009-06-07
可能是这样,我运行jruby ./test.rb 的时候,没有加入 -cp . 了:)
3 楼
RednaxelaFX
2009-06-06
嘿嘿,我知道原因了。不是因为你的脚本加了引号,你多半是直接用这帖里的脚本的对吧?那你在执行那段脚本的时候,IFoo.class、FooImpl.class和Bar.class肯定不在当前目录下。我刚才试了一下,这样执行得到的结果就跟你说的一样。这是因为JRuby在运行的时候没有找到合适的.class文件,所以Java::IFoo这样的名字就没有对应到一个实际的类,出来的结果就不对了。
多谢提醒,我得把帖子修改一下,免得误导了……一开始就得让那几个.class文件在当前目录下才行,或者是把那几个.class文件所在的位置放到JRuby执行的classpath上。
多谢提醒,我得把帖子修改一下,免得误导了……一开始就得让那几个.class文件在当前目录下才行,或者是把那几个.class文件所在的位置放到JRuby执行的classpath上。
2 楼
RednaxelaFX
2009-06-06
Arbow 写道
似乎引入了jruby中特有的class格式,不知道是否由于这样导致"Java::IfooJava_class"加载出错
奇怪……请问你运行的脚本是跟我的完全一样还是修改过?完全一样的话,生成的TestInterfaceCall.class也应该是一样的才对。但我这边生成出来的是这样的:
Compiled from "test.rb" public class TestInterfaceCall extends java.lang.Object{ public static void main(java.lang.String[]); Code: 0: new #9; //class FooImpl 3: dup 4: invokespecial #13; //Method FooImpl."<init>":()V 7: astore_1 8: aload_1 9: invokeinterface #18, 1; //InterfaceMethod IFoo.method:()V 14: new #20; //class Bar 17: dup 18: invokespecial #21; //Method Bar."<init>":()V 21: astore_2 22: aload_2 23: invokeinterface #18, 1; //InterfaceMethod IFoo.method:()V 28: return }
要注意我的脚本里,开头的Java::IFoo等的几行都是没有引号的,或许你的脚本里不小心加了引号?我只是随便猜猜,呵呵。另外你的IFoo写成/Ifoo了。
1 楼
Arbow
2009-06-05
请教一个问题。我使用JRuby1.3生成的TestInterfaceCall.class,运行时候会提示ClassNotFound,但事实上IFoo等文件已经在同一目录下了
$ java -cp . TestInterfaceCall
Exception in thread "main" java.lang.NoClassDefFoundError: Java::IfooJava_class
Caused by: java.lang.ClassNotFoundException: Java::IfooJava_class
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
Could not find the main class: TestInterfaceCall. Program will exit.
javap结果如下:
似乎引入了jruby中特有的class格式,不知道是否由于这样导致"Java::IfooJava_class"加载出错
$ java -cp . TestInterfaceCall
Exception in thread "main" java.lang.NoClassDefFoundError: Java::IfooJava_class
Caused by: java.lang.ClassNotFoundException: Java::IfooJava_class
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
Could not find the main class: TestInterfaceCall. Program will exit.
javap结果如下:
public class TestInterfaceCall extends java.lang.Object{ public static void main(java.lang.String[]); Code: 0: new #9; //class "Java::FooImplJava_class" 3: dup 4: invokespecial #13; //Method "Java::FooImplJava_class"."<init>":()V 7: astore_1 8: aload_1 9: invokeinterface #18, 1; //InterfaceMethod "Java::IfooJava_class".method:()V 14: new #20; //class "Java::BarJava_class" 17: dup 18: invokespecial #21; //Method "Java::BarJava_class"."<init>":()V 21: astore_2 22: aload_2 23: invokeinterface #18, 1; //InterfaceMethod "Java::IfooJava_class".method:()V 28: return }
似乎引入了jruby中特有的class格式,不知道是否由于这样导致"Java::IfooJava_class"加载出错
发表评论
-
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 ... -
Java 8与静态工具类
2014-03-19 08:43 16125以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10324先看看下面这个代码例子, interface IFoo { ... -
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 ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22244(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
对C语义的for循环的基本代码生成模式
2013-10-19 23:12 21729之前有同学在做龙书(第二版)题目,做到8.4的练习,跟我对答案 ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ... -
oop、klass、handle的关系
2013-07-30 17:34 0oopDesc及其子类的实例 oop : oopDesc* ... -
Nashorn各种笔记
2013-07-15 17:03 0http://bits.netbeans.org/netbea ... -
《深入理解Java虚拟机(第二版)》书评
2013-07-08 19:19 0值得推荐的中文Java虚拟机入门书 感谢作者赠与的样书,以下 ... -
豆列:从表到里学习JVM实现
2013-06-13 14:13 48100刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ...
相关推荐
2017年最新的关于jvm的书,里面还包含了如何编译openjdk部分,值得一看。
java对象在jvm中的存储情况 jvm
java 查看JVM中所有的线程的活动状况 java 查看JVM中所有的线程的活动状况
jvm源码
其二是非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容; 其三是非Stable参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用; 本文...
Java 本身提供了多种丰富的工具来帮助开发人员查看和分析 GC 以及 JVM 内存的状况。 输出GC日志 输出 GC 日志对于跟踪分析 GC 的状况,无疑是最明显和直接的分析内存回收状况的方 法,只是 GC 日志输出后需要人肉的...
java 虚拟机 简单实现 入栈 出栈 加减 功能 可以跳转
jvm 配置jvm参数 配置jvm参数
jvm-mon Console based JVM monitoring - when you just want to SSH into a server and see what's going on. jvm-top lets you monitor your JVM server applications from the terminal. Install Requirement: ...
学习关于JVM基础,java内模型的全面透析,Jar hell问题以及解决办法,Class文件格式 • Java编译执行流程 • ClassFile的格式介绍 • ClassFile中FieldInfo和MethodInfo介绍 • 类型描述Descriptor介绍 • ClassFile...
JVM 的运行机制 多线程 JVM 的内存区域 JVM 会创建操作系统的接口创建一个原生线程。JVM 线程和操作系统线程是一一对应的
本资料主要是讲解了Java虚拟机的机制,jvm设计原理与实现
jvm
从源码角度解读HotSpot的内部实现机制,本书主要包含三大部分——JVM数据结构设计与实现、执行引擎机制及内存分配模型
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。 引入Java语言虚拟机后,Java语言在不同平台...
jvm源码,jvm-native的源码,jvm支行机制,可对jvm的运行过程进行分析 个人网站:https://www.zhangjunbk.com
NeatJVM,java中的jvm实现.zip
java版本的。
JVM配置资料JVM配置资料JVM配置资料JVM配置资料
深入解析jvm深入解析jvm深入解析jvm深入解析jvm深入解析jvm深入解析jvm