`
RednaxelaFX
  • 浏览: 3049552 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

JIT编译找不到类?

阅读更多
今天开始Sun的老blog真的搬迁了,从blogs.sun.com迁移到blogs.oracle.com。结果这些迁移了的blog里的老帖像洪水般一下就把我的reader冲爆了。

不过也好,有些老帖过了一段时间重新读也会有新体会。例如这篇,Why won't JRockit find my classes

原帖里提到这样一种情况。假如在一个路径“foo”里有下面的Foo类对应的Foo.class文件:
Mattis Castergren 写道
public class Foo {
    public Foo () {
        System.out.println("Foo created");
    }
}

然后在“当前目录”下有下面的ClasspathTest类对应的Classpath.class文件:
Mattis Castergren 写道
import java.io.File;
import java.net.URLClassLoader;
import java.net.URL;
import java.lang.reflect.Method;

public class ClasspathTest {
    private static final Class[] parameters = new Class[]{URL.class};
    
    // Adds a URL to the classpath (by some dubious means)
    // method.setAccessible(true) is not the trademark of good code
    public static void addURL(URL u) throws Exception {
        Method method = URLClassLoader.class.getDeclaredMethod("addURL", parameters);
        method.setAccessible(true);
        method.invoke((URLClassLoader) ClassLoader.getSystemClassLoader(), new Object[]{u});
    }

    public static void main(String[] arg) throws Exception{
        // Add foo to the classpath, then create a Foo object
        addURL(new File("foo").toURL());
        Foo a = new Foo();
    }
}


那么用JRockit直接在当前目录运行ClasspathTest会遇到NoClassDefFoundError:
D:\test\test_hotspot_compilers\test_c2_Xcomp_classpath>C:\sdk\Java\jrmc-4.0.0-1.6.0\bin\java ClasspathTest
Exception in thread "Main Thread" java.lang.NoClassDefFoundError: Foo
        at ClasspathTest.main(ClasspathTest.java:20)

别人帖里的例子自己都验证一遍是个好习惯。这里我们实际运行JRockit R28确实看到跟描述一样的异常了。

原文同时也提到同一个程序用Oracle/Sun JDK的HotSpot VM来跑就没问题:
D:\test\test_hotspot_compilers\test_c2_Xcomp_classpath>D:\sdk\jdk1.6.0_25\bin\java ClasspathTest
Foo created


原文说,造成这个差异的原因是HotSpot VM有解释器,执行一个方法并不需要事先将该方法中所有类都加载,而可以一点点执行,到方法中间碰到某个未加载的类的时候再去加载它。例子中的代码做了件很tricky的事情:改变了系统类加载器可访问到的路径。等到HotSpot VM的解释器尝试用系统类加载器去加载Foo的时候,Foo.class已经在它能找到的路径上了。

而JRockit则不同,没有解释器,所有Java方法都必须先JIT编译了才可以开始执行。其中有个实现细节,就是JRockit要求在编译某个方法的时候就保证其中涉及的类型都加载好了。但是,上面的例子里,当JRockit的JIT编译器尝试用系统类加载器去加载Foo的时候,该类加载器还没得到“foo”这个路径,所以Foo类就加载不到了。
当然并不是说只有JIT编译器而没有解释器的设计就一定会导致这种结果,只是JRockit选择了这样实现而已。

======================================================

那么HotSpot VM里能否制造出同样的情况呢?我们知道,HotSpot VM可以通过-Xcomp,或者诸如-XX:+UseCompiler -XX:-UseInterpreter -XX:-BackgroundCompilation这样的参数强行指定(尽量)不使用解释器而(尽量)只用JIT编译器来处理Java程序的执行。
在这种条件下HotSpot VM会不会也跟JRockit一样,执行上面的例子报错说找不到类呢?

让我们试试看。下面的例子都是在32位Windows XP上用JDK 6 update 25跑的:
D:\test\test_hotspot_compilers\test_c2_Xcomp_classpath>D:\sdk\jdk1.6.0_25\bin\java -server -Xcomp ClasspathTest
Foo created

嗯?没报错。

那有没有可能是HotSpot VM实际上没编译ClasspathTest.main()方法呢?用-XX:+PrintCompilation来看:
java -server -Xcomp -XX:+PrintCompilation ClasspathTest

...
    567 270   b   java.lang.reflect.Method::getModifiers (5 bytes)
    567 271   b   ClasspathTest::main (24 bytes)
    567 271      made not entrant  ClasspathTest::main (24 bytes)
    567 272   b   java.io.File::toURL (23 bytes)
    567 272      made not entrant  java.io.File::toURL (23 bytes)
    568 273   b   java.io.File::getAbsolutePath (8 bytes)
...

有的,这个main()方法编译了。

但是,HotSpot的server编译器到底给main()方法生成了怎样的代码呢?用-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly来看:(用法参考以前一帖
java -server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly ClasspathTest

  # {method} 'main' '([Ljava/lang/String;)V' in 'ClasspathTest'
  # parm0:    ecx       = '[Ljava/lang/String;'
  #           [sp+0x10]  (sp of caller)
  0x00a33b00: mov    %eax,-0x3000(%esp)
  0x00a33b07: push   %ebp
  0x00a33b08: sub    $0x8,%esp          ;*synchronization entry
                                        ; - ClasspathTest::main@-1 (line 19)
  0x00a33b0e: mov    $0xa,%ecx
  0x00a33b13: call   0x0099c700         ; OopMap{off=24}
                                        ;*new  ; - ClasspathTest::main@0 (line 19)
                                        ;   {runtime_call}
  0x00a33b18: int3                      ;*new  ; - ClasspathTest::main@0 (line 19)

可以看到,生成的代码只做了这么几件事情:
1、检查调用栈是否要溢出了(0x00a33b00)
2、保存老栈帧信息,建立新栈帧(0x00a33b07-0x00a33b08)
3、?(0x00a33b0e)
4、不应该跑到这里来,否则中断(0x00a33b18)

这问号就是有趣的地方了。如果这个问号表示的代码不足以执行main()的内容的话,那main()到底是如何被执行的呢?

我们可以用另一组参数来了解这神秘的“0x0099c700”地址到底是什么东西,-XX:+UnlockDiagnosticVMOptions -XX:+PrintStubCode
java -server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintStubCode ClasspathTest

Decoding UncommonTrapBlob@0x0099c700 0x0099c6c8
[Disassembling for mach='i386']
  0x0099c700: sub    $0xc,%esp
  0x0099c703: mov    %ebp,0x8(%esp)
  0x0099c707: emms   
  0x0099c709: mov    %fs:0x0(,%eiz,1),%edx
  0x0099c711: mov    -0xc(%edx),%edx
  0x0099c714: mov    %esp,0x118(%edx)
  0x0099c71a: mov    %edx,(%esp)
  0x0099c71d: mov    %ecx,0x4(%esp)
  0x0099c721: call   0x6dc77300
  0x0099c726: mov    %fs:0x0(,%eiz,1),%ecx
  0x0099c72e: mov    -0xc(%ecx),%ecx
  0x0099c731: movl   $0x0,0x118(%ecx)
  0x0099c73b: mov    %eax,%edi
  0x0099c73d: add    $0xc,%esp
  0x0099c740: mov    (%edi),%ecx
  0x0099c742: add    %ecx,%esp
  0x0099c744: mov    0xc(%edi),%ebx
  0x0099c747: mov    %esp,%ecx
  0x0099c749: mov    %ebx,-0x1000(%ecx)
  0x0099c74f: sub    $0x1000,%ecx
  0x0099c755: sub    $0x1000,%ebx
  0x0099c75b: jg     0x0099c749
  0x0099c75d: mov    %ebx,(%ecx)
  0x0099c75f: mov    %ebx,-0x1000(%ecx)
  0x0099c765: mov    0x14(%edi),%ecx
  0x0099c768: pop    %esi
  0x0099c769: mov    0x10(%edi),%esi
  0x0099c76c: mov    0x8(%edi),%ebx
  0x0099c76f: mov    %ebx,0x20(%edi)
  0x0099c772: mov    0x24(%edi),%ebp
  0x0099c775: mov    %esp,0x2c(%edi)
  0x0099c778: mov    0x4(%edi),%ebx
  0x0099c77b: sub    %ebx,%esp
  0x0099c77d: mov    (%esi),%ebx
  0x0099c77f: sub    $0x8,%ebx
  0x0099c782: pushl  (%ecx)
  0x0099c784: push   %ebp
  0x0099c785: mov    %esp,%ebp
  0x0099c787: sub    %ebx,%esp
  0x0099c789: mov    0x2c(%edi),%ebx
  0x0099c78c: movl   $0x0,-0x8(%ebp)
  0x0099c793: mov    %ebx,-0x4(%ebp)
  0x0099c796: mov    %esp,0x2c(%edi)
  0x0099c799: add    $0x4,%esi
  0x0099c79c: add    $0x4,%ecx
  0x0099c79f: decl   0x20(%edi)
  0x0099c7a2: jne    0x0099c77d
  0x0099c7a4: pushl  (%ecx)
  0x0099c7a6: push   %ebp
  0x0099c7a7: mov    %esp,%ebp
  0x0099c7a9: sub    $0x8,%esp
  0x0099c7ac: mov    %fs:0x0(,%eiz,1),%edi
  0x0099c7b4: mov    -0xc(%edi),%edi
  0x0099c7b7: mov    %ebp,0x120(%edi)
  0x0099c7bd: mov    %esp,0x118(%edi)
  0x0099c7c3: mov    %edi,(%esp)
  0x0099c7c6: movl   $0x2,0x4(%esp)
  0x0099c7ce: call   0x6dc75d40
  0x0099c7d3: mov    %fs:0x0(,%eiz,1),%edi
  0x0099c7db: mov    -0xc(%edi),%edi
  0x0099c7de: movl   $0x0,0x118(%edi)
  0x0099c7e8: movl   $0x0,0x120(%edi)
  0x0099c7f2: mov    %ebp,%esp
  0x0099c7f4: pop    %ebp
  0x0099c7f5: ret    
  0x0099c7f6: .byte 0xc1
  0x0099c7f7: .byte 0x4


嗯,很长很诡异的代码对吧?实际上这块代码是一个“uncommon trap”,也就是当HotSpot的server编译器遇到它觉得不会发生的、或不方便处理的部分的时候会用到的一块代码。
前面提到的“问号”的代码,就是用于跳进该“uncommon trap”的。

但main()方法为啥刚一进去就要无条件跳进一个uncommon trap呢?这次我们换用一个fastdebug版的JDK 6 update 25,然后用-XX:+PrintOptoAssembly来看看:
java -server -Xcomp -XX:+PrintOptoAssembly ClasspathTest

000   B1: #	N1 <- BLOCK HEAD IS JUNK   Freq: 1
000   	# stack bang
	PUSHL  EBP
	SUB    ESP,8	# Create frame
00e   	MOV    ECX,#10
013   	CALL,static  wrapper for: uncommon_trap(reason='unloaded' action='reinterpret' index='10')
        # ClasspathTest::main @ bci:0  L[0]=_ L[1]=_
        # OopMap{off=24}
018   	INT3   ; ShouldNotReachHere
018

代码里嵌着的注释说得很明白了,跳进uncommon trap的原因是“unloaded”,也就是该方法需要用的类要么尚未加载,要么虽然加载过但已经卸载掉了。相应采取的措施就是“reinterpret”,也就是退回到解释器去执行。

具体做检查的代码在这里:
void ciTypeFlow::StateVector::do_new(ciBytecodeStream* str) {
  bool will_link;
  ciKlass* klass = str->get_klass(will_link);
  if (!will_link || str->is_unresolved_klass()) {
    trap(str, klass, str->get_klass_index());
  } else {
    push_object(klass);
  }
}

也就是HotSpot的JIT编译器做初步类型分析的时候就已经发现有问题了。

看到了么?HotSpot的server编译器只是等同于给例子里的main()生成了个解释器入口,让它跳进去而已,并没有真的把main()方法编译出来。

所以说HotSpot的server编译器其实跟JRockit的JIT编译器在这点上都偷懒了…
只不过HotSpot能退回到解释器里,所以编译器偷懒了没关系,程序还能跑,就是慢一些;JRockit的编译器偷懒了,Java程序搞不好就得撞墙了

HotSpot的client编译器在遇到编译时仍未resolve的符号的话,会在使用这些符号的位置生成一个call thunk,调用一个用于符号解析的thunk,解析好之后把直接引用patch回到原本的call thunk上。这样不用只为解决符号引用的resolution就掉回到解释器里,但生成的代码质量也会稍微差一些。

P.S. 顺带一提:HotSpot的server编译器并不会在接受某个方法的编译请求时强制要求里面涉及的所有类型已经被加载,只要求在方法签名里出现的类型在编译前已经被加载,并要求在编译前该方法里涉及的所有的字符串常量已经resolve完(具体执行这些约束的时负责分派编译请求的CompilerBroker而不是server编译器自身,见回复里的讨论)。
所以这个例子就算给了-classpath ".;foo"也会得到一样的、只含一个uncommon trap的main()方法。

P.P.S. 顺带找个地方记下ciTypeFlow的作用:
http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2008-May.txt
引用
From John.Rose at Sun.COM  Fri May 16 11:23:22 2008
From: John.Rose at Sun.COM (John Rose)
Date: Fri, 16 May 2008 11:23:22 -0700
Subject: Failing typeflow responsibility assertions
In-Reply-To: <20080516084237.GA3704@redhat.com>
References: <20080516084237.GA3704@redhat.com>
Message-ID: <200E1632-3850-445C-B32E-7A0F6A497BAC@sun.com>

On May 16, 2008, at 1:42 AM, Gary Benson wrote:

> Do I have to do something to tell the CompileBroker not
> to feed me methods full of unloaded stuff?  Oh, and I'm using -Xmixed.

The typeflow pass builds the basic block model and typestates 
consumed by the parser.  You can't build phis correctly without that 
stuff.

(An earlier version tried to parse, find blocks, and compute phi 
types all on the fly, but it didn't work out, so ciTypeFlow was added 
as a first pass.)

(The new typestate maps might change this a little, but we have to 
compile w/o the maps anyway.)

The basic block model is pruned at unreached bytecodes which would 
provoke class loading.

As a result, the parser can (mostly) ignore unloaded classes, which 
removes lots of buggy edge cases.

Do you use ciTypeFlow in your JIT?

-- John


From John.Rose at Sun.COM  Tue May 20 10:59:04 2008
From: John.Rose at Sun.COM (John Rose)
Date: Tue, 20 May 2008 10:59:04 -0700
Subject: Failing typeflow responsibility assertions
In-Reply-To: <20080520074445.GA3861@redhat.com>
References: <20080516084237.GA3704@redhat.com>
<200E1632-3850-445C-B32E-7A0F6A497BAC@sun.com>
<20080520074445.GA3861@redhat.com>
Message-ID: <E0C35D5A-DA47-48AD-BA2D-EFC6F6F4A720@sun.com>

On May 20, 2008, at 12:44 AM, Gary Benson wrote:

> That's awesome!  I wasn't using it until yesterday but I am now

I'm glad it's helpful!

> So does the ciTypeFlow pass actually load the unloaded classes for
> you?

No, the JIT tries pretty hard not to load classes.  IIRC, the only 
exception to this rule is the call to load_signature_classes in 
compileBroker.cpp.

JIT compilation should be transparent to Java execution, but loading 
classes causes class loader code to execute.  If the JIT causes 
bytecode execution, then the JIT can cause application state changes, 
which explores new application states unnecessarily.  This can expose 
JIT-entangled bugs in the application.  You want this in stress 
testing, but not in the field.

The JVM spec. allows class loading--not initialization--for any 
reason, but it's better (for system reproducibility) if the JIT has 
no detectable effect on app. state except speedups.

-- John

引用
From John.Rose at Sun.COM  Thu May 22 11:02:35 2008
From: John.Rose at Sun.COM (John Rose)
Date: Thu, 22 May 2008 11:02:35 -0700
Subject: Failing typeflow responsibility assertions
In-Reply-To: <20080522091725.GB3794@redhat.com>
References: <20080516084237.GA3704@redhat.com>
<200E1632-3850-445C-B32E-7A0F6A497BAC@sun.com>
<20080520074445.GA3861@redhat.com>
<E0C35D5A-DA47-48AD-BA2D-EFC6F6F4A720@sun.com>
<20080522091725.GB3794@redhat.com>
Message-ID: <3950F6EA-B65B-485F-B336-D64F048B681E@sun.com>

On May 22, 2008, at 2:17 AM, Gary Benson wrote:

> So the 'assert(will_link, "typeflow responsibility")' bits aren't
> being hit because the typeflow pass bails out?  (ie env()->failing()
> returns true?)  Will the compile broker retry the compilation later?

No, typeflow ends such a block with a "trap".  (If we bailed out on 
every unloaded class, we'd almost never compile anything.)  A trap is 
like a branch to the interpreter (deopt).  The parser is responsible 
for respecting those trap markings.  A basic block that ends in a 
trap never gets (in compiled code) to the branch or return that ends 
the basic block in the bytecodes.

-- John
分享到:
评论
11 楼 RednaxelaFX 2011-05-13  
wkoffee 写道
引用
wkoffee 写道
严格的说jrockit的做法是不符合java规范的,jvm的规范要求在第一次访问类的时候加载,这个程序在执行new Foo的时候classpath已经设好了,classloader应该可以加载类,所以这是个合法的程序。jrockit简单的退出是有问题的。

先前没仔细看这条回复,请问严格来说JRockit的这个做法是违反了第几版JVM规范的哪部分?给我的感觉是一个只能通过强行调用non-public方法做到的事情不会是规范里会写到的。

在jvm spec 2.17.2 中
"The loading process is implemented by the class ClassLoader and its subclasses. Different subclasses of ClassLoader may implement different loading policies. In particular, a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together. These activities may not be completely transparent to a running application if, for example, a newly compiled version of a class is not found because an older version is cached by a class loader. It is the responsibility of a class loader, however, to reflect loading errors only at points in the program where they could have arisen without prefetching or group loading.
If an error occurs during class loading, then an instance of one of the following subclasses of class LinkageError will be thrown at any point in the program that (directly or indirectly) uses the type:"
而且下面列出NoClassDefFoundError是loading的error,那它应该在用到该类型的时候抛出。在这个例子中,main方法执行的入口时候并没有用到Foo,只有在new Foo的时候用到,jrockit的做法相当于prefetch或者group loading,但jvm有责任确保不应该因为这个引起额外的异常,(这里有点绕,我理解是如果有异常,那不使用prefetch or group loading的classloader也会报错)

这个例子里使用反射不知道是不是很重要,URLClassLoader的addUrl方法是protected的,自定义classloader就可以绕过它。但不知道是不是因为用了反射才会出现这个结果。

前面的解释都说得通。但我觉得问题点在于system class loader到底是个什么类型的对象,在JVM规范里没定义,所以本来Java应用不应该能够假定它会是URLClassLoader的子类的实例。这样的话,依赖该class loader有addURL方法、并且通过它修改class path的做法的结果会怎样本来就应该也是未定义的吧。

刚试着搜了一下,在JVM规范里没找到"system class loader"的定义。这玩儿是在哪里定义的呢?
10 楼 wkoffee 2011-05-12  
引用
但CompilerBroker在tiered compilation里多数时候都在跟compiled code打交道,从compiled code里跟compiler打交道算不算是整个compilation system的一部分就模糊了。

能详细说说这一点吗,我印象中CompilerBroker只是发起请求,tiered compilation时为什么compiled code要和它打交道。
9 楼 wkoffee 2011-05-12  
引用
wkoffee 写道
严格的说jrockit的做法是不符合java规范的,jvm的规范要求在第一次访问类的时候加载,这个程序在执行new Foo的时候classpath已经设好了,classloader应该可以加载类,所以这是个合法的程序。jrockit简单的退出是有问题的。

先前没仔细看这条回复,请问严格来说JRockit的这个做法是违反了第几版JVM规范的哪部分?给我的感觉是一个只能通过强行调用non-public方法做到的事情不会是规范里会写到的。

在jvm spec 2.17.2 中
"The loading process is implemented by the class ClassLoader and its subclasses. Different subclasses of ClassLoader may implement different loading policies. In particular, a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together. These activities may not be completely transparent to a running application if, for example, a newly compiled version of a class is not found because an older version is cached by a class loader. It is the responsibility of a class loader, however, to reflect loading errors only at points in the program where they could have arisen without prefetching or group loading.
If an error occurs during class loading, then an instance of one of the following subclasses of class LinkageError will be thrown at any point in the program that (directly or indirectly) uses the type:"
而且下面列出NoClassDefFoundError是loading的error,那它应该在用到该类型的时候抛出。在这个例子中,main方法执行的入口时候并没有用到Foo,只有在new Foo的时候用到,jrockit的做法相当于prefetch或者group loading,但jvm有责任确保不应该因为这个引起额外的异常,(这里有点绕,我理解是如果有异常,那不使用prefetch or group loading的classloader也会报错)

这个例子里使用反射不知道是不是很重要,URLClassLoader的addUrl方法是protected的,自定义classloader就可以绕过它。但不知道是不是因为用了反射才会出现这个结果。
8 楼 RednaxelaFX 2011-05-12  
wkoffee 写道
发起compiler请求到底算不算compiler的职责,每个人可以自己理解,我个人认为不算。

嗯,CompilerBroker名字都已经这么叫了,也就是个转发者的角色。如果说它是解释器与JIT编译器之间的接口上的东西也对,那么两边都算不上。但CompilerBroker在tiered compilation里多数时候都在跟compiled code打交道,从compiled code里跟compiler打交道算不算是整个compilation system的一部分就模糊了。

不过你说得对,我应该把正文的最后那段改改,强调只是个约束而不是opto主动发起的动作。

wkoffee 写道
严格的说jrockit的做法是不符合java规范的,jvm的规范要求在第一次访问类的时候加载,这个程序在执行new Foo的时候classpath已经设好了,classloader应该可以加载类,所以这是个合法的程序。jrockit简单的退出是有问题的。

先前没仔细看这条回复,请问严格来说JRockit的这个做法是违反了第几版JVM规范的哪部分?给我的感觉是一个只能通过强行调用non-public方法做到的事情不会是规范里会写到的。
7 楼 RednaxelaFX 2011-05-11  
wkoffee 写道
这个要求究竟有多大实际意义也很可疑,正常的java程序在发起请求的时候这些类应该都已加载过,即使有个别极端情况没有加载,compiler简单的bailout就可以,所以我觉得这个看上去更像是为了做测试所加的内容。

如果不是-Xcomp的话,以默认的mixed mode是不应该遇到方法签名上有未加载的类型的。就这点而言在HotSpot上跑的一般Java程序是不会遇到帖子里说的问题的。

话说回来,原本为啥会弄CTW模式来测试HotSpot的编译器的…CTW也确实就是只“为了做测试”而存在的模式,而那个模式下会出现很多奇怪的情况,跟-Xcomp有相似之处
6 楼 wkoffee 2011-05-11  
发起compiler请求到底算不算compiler的职责,每个人可以自己理解,我个人认为不算。
这个要求究竟有多大实际意义也很可疑,正常的java程序在发起请求的时候这些类应该都已加载过,即使有个别极端情况没有加载,compiler简单的bailout就可以,所以我觉得这个看上去更像是为了做测试所加的内容。
5 楼 RednaxelaFX 2011-05-11  
wkoffee 写道
这段代码是生成编译请求的工作,其实还是java thread在执行,compiler thread不会执行的。从注释看是c2的prerequesites,只是一些细节的东西,并不值得深入。

嗯,compiler thread是不会调用Java-level方法的。不过前面也一直没说过那些加载动作是在compiler thread上做的吧…是怎么掉进这段对话的orz

值不值得深入要看目的了。如果关注opto里的很多奇怪的约束,免得改代码的时候不小心弄坏的话,那各种细节都不得不留意。除此之外的话…
4 楼 wkoffee 2011-05-11  
这段代码是生成编译请求的工作,其实还是java thread在执行,compiler thread不会执行的。从注释看是c2的prerequesites,只是一些细节的东西,并不值得深入。
3 楼 RednaxelaFX 2011-05-10  
wkoffee 写道
引用
P.S. 顺带一提:HotSpot的server编译器并不会在编译某个方法前强制加载里面涉及的所有类型,只会强制加载在方法签名里出现的类型,并强制resolve掉所有的字符串常量。所以这个例子就算给了-classpath ".;foo"也会得到一样的、只含一个uncommon trap的main()方法。

印象中hotspot的compiler不负责加载任何java类,加载类需要执行类的<clinit>方法,compiler thread是不会执行任何java方法的,所以应该还是java thread来加载类。

可以看看CompilerBroker的实现。脏活累活opto都交给别人干了 XD
hotspot/src/share/vm/compiler/compileBroker.cpp
     1031   // some prerequisites that are compiler specific
     1032   if (compiler(comp_level)->is_c2()) {
     1033     method->constants()->resolve_string_constants(CHECK_0);
     1034     // Resolve all classes seen in the signature of the method
     1035     // we are compiling.
     1036     methodOopDesc::load_signature_classes(method, CHECK_0);
     1037   }
2 楼 wkoffee 2011-05-10  
引用
P.S. 顺带一提:HotSpot的server编译器并不会在编译某个方法前强制加载里面涉及的所有类型,只会强制加载在方法签名里出现的类型,并强制resolve掉所有的字符串常量。所以这个例子就算给了-classpath ".;foo"也会得到一样的、只含一个uncommon trap的main()方法。

印象中hotspot的compiler不负责加载任何java类,加载类需要执行类的<clinit>方法,compiler thread是不会执行任何java方法的,所以应该还是java thread来加载类。
1 楼 wkoffee 2011-05-10  
严格的说jrockit的做法是不符合java规范的,jvm的规范要求在第一次访问类的时候加载,这个程序在执行new Foo的时候classpath已经设好了,classloader应该可以加载类,所以这是个合法的程序。jrockit简单的退出是有问题的。

相关推荐

    深入JVM系列-JIT编译详解1

    "深入JVM系列-JIT编译详解1" 在这篇文章中,我们将深入探讨JVM中的JIT编译技术。JIT(Just In Time)编译器是一种即时编译技术,它可以加速Java程序的执行速度。 JIT编译过程 -------- 在执行Java程序时,JVM会将...

    你的Java代码对JIT编译友好么?

    Jarscan能够静态分析JAR文件,找出可能无法被JIT编译的方法。此外,启用JVM的`-XX:+PrintCompilation`选项可以在运行时记录编译信息,帮助开发者理解哪些方法正在被JIT编译。 结合Jarscan和`PrintCompilation`的...

    Trace-based JIT简介(对Method JIT的改进)

    3. **Trace编译**:将提取到的热点路径编译成本地机器码。 4. **Trace合并**:将编译后的Trace重新组合成可执行的代码序列。 5. **动态更新**:根据程序执行情况的变化动态调整已编译的Trace。 #### 性能评估 为了...

    java内存动态编译执行

    JIT编译有两个主要类型:客户端编译器(Client Compiler),适用于快速启动但对性能要求不高的场景;服务器编译器(Server Compiler),延迟启动,优化程度更高,适合长时间运行的应用。 3. **热点代码识别**: ...

    Jit.rar_android

    1. **JIT编译过程**:在Android中,Dalvik虚拟机(或者后来的ART,Android RunTime)使用了JIT技术。当应用程序首次运行时,字节码被解释执行,但随着运行,JIT会收集执行信息,对热点代码进行编译,以提高后续执行...

    JIT实现拓扑展现

    在IT行业中,"JIT实现拓扑展现"这个主题涉及到的是动态编译技术和网络拓扑图的可视化。这里,我们主要探讨JIT(Just-In-Time)编译器以及如何利用它来优化程序性能,同时也会关注如何通过编程手段将网络拓扑结构以...

    Writing JIT-Spray Shellcode for fun and profit

    攻击者可以利用这一点,在JIT编译过程中注入恶意代码,从而绕过DEP和ASLR,实现对目标系统的控制。 ### IE8的保护机制 Internet Explorer 8引入了永久性的DEP保护,这使得传统的Heap Spray攻击方法失效。IE8通过...

    JIT Spray技术.pdf

    本文档探讨了两种新型技术来绕过这些缓解措施:指针推断(Pointer Inference)和即时编译喷射(JIT Spraying)。这两种技术充分利用了浏览器内广泛使用的高级脚本解释器或虚拟机的攻击面。 #### 关键技术介绍 ####...

    Android Framework介绍 Dalvik原理 JIT介绍

    - **编译时机**:JIT编译器并不是一开始就将所有代码编译成机器码,而是根据代码的热冷程度来决定何时进行编译。这样可以避免不必要的编译开销。 - **编译策略**:通常,JIT编译器会监控代码执行频率,并将最频繁...

    riscv-jit-emulator:用于riscv的WIP模拟器(最终将)进行jit编译代码-目的是在沙箱中运行cargorustc

    在riscv-jit-emulator中,JIT编译被用来动态转换RISC-V指令,以便在模拟器内部更快速地执行。 沙箱环境是运行代码的一种安全机制,它可以限制程序的访问权限,防止它们对系统造成破坏或溢出。在这个模拟器中,...

    深入剖析ASP.NET的编译原理.rar

    对于未预编译的ASP.NET应用,当用户首次访问一个页面时,该页面及其依赖项会被JIT编译。JIT编译器会将IL代码转换为特定平台的机器码,确保代码运行效率。 3. **ASP.NET的编译过程**: - **标记编译(Markup ...

    编译系统透视 图解编译原理

    《编译系统透视:图解编译原理》是深入理解计算机科学中不可或缺的一环,它为我们揭示了程序语言从源代码到机器可执行代码的转化过程。编译器是这个转化过程的核心,它将高级语言翻译成计算机可以直接执行的低级语言...

    反编译VB程序

    5. JIT编译:运行时,Just-In-Time编译器将IL代码编译成针对特定平台的机器码。 反编译VB程序主要涉及以下几个方面: 1. 使用反编译工具:市面上有一些专门用于反编译.NET程序的工具,如Reflector、ILSpy、dotPeek...

    sympy-llvm:JIT编译SymPy表达式以加快数值评估的速度

    3. **JIT编译**:接下来,LLVM的JIT编译器将IR编译成特定平台的机器码,这个过程几乎是即时完成的。 4. **数值计算**:最后,生成的机器码可以直接执行,对给定的数值进行快速计算。 使用sympy-llvm的优势在于,它...

    动态编译java

    Java动态编译是一种在程序运行时将源代码转换为字节码的技术,它使得程序...无论是JVM内置的JIT编译机制,还是第三方库提供的动态代码生成工具,都是Java生态系统中不可或缺的一部分,它们共同推动了Java技术的发展。

    Mono-mbe版源码编译.pdf

    与传统的解释执行不同,JIT编译会将编译后的代码缓存起来,以便再次使用时无需重新编译,从而提高效率。JIT编译技术使得动态更新代码成为可能,尤其是在Android平台上。 ### Unity不同设置对应的Mono源码选择和编译...

    bpf_jit_comp.rar_legacy

    JIT编译是BPF的一个重要特性,它将BPF字节码转换为机器码,以提高执行效率。BPF JIT编译器将BPF程序转换为特定架构的本地代码,从而在内核中运行时无需解释执行,提高了性能。 在`board-generic.c`文件中,我们可能...

    反编译工具,可以反编译所有的C#写的程序

    由于反编译可能会侵犯到原软件的版权,因此在没有得到授权的情况下,商业用途的反编译行为可能涉及到法律风险。在进行此类操作时,确保遵循合法和道德的规范,尊重他人的知识产权。 总的来说,反编译工具为开发者...

    反编译软件能反编译.net代码

    "反编译软件能反编译.NET代码"这一主题涉及到的主要知识点包括反编译原理、.NET代码的可逆工程以及相关的反编译工具。 首先,我们需要理解什么是反编译。反编译是将已编译的机器语言代码转换回高级语言的过程,其...

    动态编译与性能测量1

    对于Java开发者而言,这意味着简单的微基准测试可能无法准确反映实际性能,因为它们可能未考虑到JIT编译的影响。因此,设计全面的性能测试需要考虑代码执行的热身阶段、JIT编译的触发条件以及各种优化技术的作用。 ...

Global site tag (gtag.js) - Google Analytics