`
RednaxelaFX
  • 浏览: 3016040 次
  • 性别: 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简单的退出是有问题的。

相关推荐

    simple-jit-compiler:该项目旨在说明 JIT 编译器开发中使用的机制

    (例如: gcc jit.c -o jit -m32 ) 但是,这具有体系结构限制,并且不适用于任何地方。x86 指令反汇编程序集或编译代码向您显示指令和实现它们的机器代码。 对于给定的指令,不仅有数百种不同的x86指令,而且可能...

    gsr-jit:测试JIT编译器

    GSR可以在不到一毫秒的时间内编译文件,这对于快速迭代很重要。 我尝试为此使用正式的Rust编译器并使用LLVM,但是它不起作用,无法直接输出asm,只能编译为ELF二进制文件或类似文件。 句法 GSR使用syn解析器,遵循...

    PHP编译安装中遇到的两个错误和解决方法

    提示在/usr/lib 下找不到相关模块,这是因为64位的linux默认把以上文件都存在 /usr/lib64 文件夹下。 解决方法: 复制代码 代码如下: cp -frp /usr/lib64/libldap* /usr/lib/ 重新configure即可 二、P

    jit-compiler:Go中的JIT编译器

    即时编译器 这是一个Golang库,其中包含x86-64汇编程序(请参见“ asm /”)和可编译为x86-64(请参见“ ir /”)的高级中间表示形式。动机该项目的初衷是能够将复杂的Sequencer和Synthesizer定义编译为机器代码(请...

    llvmlite, 用于编写JIT编译器的轻量级 LLVM python 绑定.zip

    llvmlite, 用于编写JIT编译器的轻量级 LLVM python 绑定 llvmlite 用于编写JIT编译器的轻量级 LLVM python 绑定旧的绑定公开了很多 LLVM,但是把C -style内存管理映射到 python 是容易出错的。 Numba 和许多JIT...

    mvnd-0.9.0-windows-amd64

    mvnd属于maven的子项目,目的是为了提高构建性能,且...GraalVM的JIT(Just In Time)实时编译特性也被运用到Maven构建作业中。JIT可以大大降低编译时间,在重复构建过程中,JIT优化代码立即可用,也极大提高了构建效率。

    cranelift-jit-demo:使用Cranelift的玩具语言的JIT编译器和运行时

    这是一个简单的演示,它使用Cranelift来JIT编译玩具语言。 它在使用新的JIT接口进行开发。 JIT提供了一个相对简单的API,负责管理符号表,分配内存和执行重定位。 这部分受到Ulysse Carion的和Jonathan Turner的的...

    XLua_2018最新版

    目前luajit作者不打算维护luajit,在找人接替其维护,后续发展不太明朗。 项目可以根据自己情况判断哪个更适合。因为目前lua53版本使用较多,所以xLua工程Plugins目录下默认配套是lua53版本。 ## 更多示例 * [01_...

    coat:C ++的EDSL使即时代码生成更加容易

    外套:COdegen抽象类型 COAT是C ++的嵌入式领域特定语言(EDSL),可简化即时(JIT)代码的生成。 它提供抽象类型和控制流... 这几乎不是JIT编译的一个很好的应用程序,但是它显示了在COAT的帮助下,代码生成的简单性

    c#学习笔记.txt

    一个结构不能从另一个结构或类继承,而且不能作为一个类的基。但是,结构从基类 Object 继承。结构可实现接口,其方式同类完全一样。 [c ] 与 C 不同,无法使用 struct 关键字声明类。在 C# 中,类与结构在语义上是...

    CudaPy:CudaPy是一个运行时库,可让Python程序员访问NVIDIA的CUDA并行计算API。

    本机:您不必编写甚至看不到C ++代码。 CudaPy内核是纯粹用Python(的)编写的。 内核调用是通过调用Python函数来完成的。 动态:不需要编译。 只需启动您的解释器,CudaPy将通过JIT编译您的内核。 高效:CudaPy ...

    java-jdk-hotspot源码

    它来源于Strongtalk VM, 而这款虚拟机中相当多的技术又是来源于一款支持Self语言实现“达到C语言50%以上的执行效率”的目标而设计的虚拟机, Sun公司注意到了这款虚拟机在JIT编译上有许多优秀的理念和实际效果,...

    MSIL(Microsoft Intermediate Language)中文教程

    尽管它不是真正意义上的底层汇编语言,不能分析 JIT Complier 编译后的 Native Code,但总算能一窥托管编译器的奥秘。学习 MSIL 的目的,并不是真的要用它去编码,而是为了更好地 "交流"。 这些相关的MSIL都是我从...

    基于PCRE2的正则表达式模块 支持Unicode-易语言

    启用JIT编译在编译时稍微多耗费一些时间,但在匹配时速度快得多,这通常运用于单个模式进行多次匹配时需要 关于命名子表达式: 表达式允许加入‘命名标签’,使用命名标签的格式:(?(子表达式)) 例如表达式:(?( [1-...

    c# 常量和字段

    它的值是在编译时确定的。编译器将常量保存到程序集的元数据中,所有只能是编译器认识的基元类型作为常量。... 通常是在引用该类型的任何方法首次进行JIT编译的时候。 实例字段:用于容纳字段数据的动态内存是

    使用Python写CUDA程序的方法

    Numba通过及时编译机制(JIT)优化Python代码,Numba可以针对本机的硬件环境进行优化,同时支持CPU和GPU的优化,并且可以和Numpy集成,使Python代码可以在GPU上运行,只需在函数上方加上相关的指令标记, ...

    kitti-object-eval-python:在Python中快速进行kitti对象检测评估(在不到10秒的时间内完成评估)

    在python中快速进行kitti对象检测评估(在不到10秒的时间内完成评估),支持2d / bev / 3d / aos。 ,支持可可式AP。 如果使用命令行界面,numba需要一些时间来编译jit函数。 警告:“ coco”不是官方指标。 仅“ ...

    C#微软培训资料

    第四章 数 据 类 型 .28 4.1 值 类 型 .28 4.2 引 用 类 型 .33 4.3 装箱和拆箱 .39 4.4 小 结 .42 第五章 变量和常量 .44 5.1 变 量 .44 5.2 常 量 .46 5.3 小 结 .47 第六章 类 型 转 换 .48 ...

    Facebook开源的高效的PHP运行环境HHVM.zip

    HHVM即HipHop Virtual Machine,目前是3.0版本,出自Facebook,它先将PHP和Hack程序编译为中间字节码,然后通过just-in-time (JIT)编译器将字节码动态翻译为x64机器代码,应用上了非常多的编译优化技术。 JIT就是...

    Mun 语言和运行时的源代码。

    特征提前编译- Mun 提前编译 (AOT),而不是及时解释或编译 (JIT)。通过在 AOT 编译期间检测代码中的错误,消除了一整类运行时错误。这使开发人员可以在他们的 IDE 中保持舒适,而不必在 IDE 和目标应用程序之间切换...

Global site tag (gtag.js) - Google Analytics