- 浏览: 3052122 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (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分享的概要
刚才在JavaEye问答频道看到了这么一个问题:
这是Sun实现的Java标准库的一个细节。下面举例稍微讲解一下。
假如有这么一个类A:
可以编写另外一个类来反射调用A上的方法:
注意到TestClassLoad类上不会有对类A的符号依赖——也就是说在加载并初始化TestClassLoad类时不需要关心类A的存在与否,而是等到main()方法执行到调用Class.forName()时才试图对类A做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的ClassLoader来加载,并且初始化新加载的类。……好吧这个细节跟主题没啥关系。
回到主题。这次我的测试环境是Sun的JDK 1.6.0 update 13 build 03。编译上述代码,并在执行TestClassLoad时加入-XX:+TraceClassLoading参数(或者-verbose:class或者直接-verbose都行),如下:
可以看到输出了一大堆log,把其中相关的部分截取出来如下:(完整的log可以从附件下载)
可以看到前15次反射调用A.foo()方法并没有什么稀奇的地方,但在第16次反射调用时似乎有什么东西被触发了,导致JVM新加载了一堆类,其中就包括[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]这么一行。这是哪里来的呢?
先来看看JDK里Method.invoke()是怎么实现的。
java.lang.reflect.Method:
可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。
每个实际的Java方法只有一个对应的Method对象作为root,。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。在第一次调用一个实际Java方法对应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。
那么MethodAccessor是啥呢?
sun.reflect.MethodAccessor:
可以看到它只是一个单方法接口,其invoke()方法与Method.invoke()的对应。
创建MethodAccessor实例的是ReflectionFactory。
sun.reflect.ReflectionFactory:
这里就可以看到有趣的地方了。如注释所述,实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。
Sun的JDK是从1.4系开始采用这种优化的,主要作者是Ken Russell
上面看到了ReflectionFactory.newMethodAccessor()生产MethodAccessor的逻辑,在“开头若干次”时用到的DelegatingMethodAccessorImpl代码如下:
sun.reflect.DelegatingMethodAccessorImpl:
这是一个间接层,方便在native与Java版的MethodAccessor之间实现切换。
然后下面就是native版MethodAccessor的Java一侧的声明:
sun.reflect.NativeMethodAccessorImpl:
每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。
注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的:
其中的关键又是Reflection::invoke_method():
再下去就深入到HotSpot VM的内部了,本文就在这里打住吧。有同学有兴趣深究的话以后可以再写一篇讨论native版的实现。
回到Java的一侧。MethodAccessorGenerator长啥样呢?由于代码太长,这里就不完整贴了,有兴趣的可以到OpenJDK 6的Mercurial仓库看:OpenJDK 6 build 17的MethodAccessorGenerator。它的基本工作就是在内存里生成新的专用Java类,并将其加载。就贴这么一个方法:
去阅读源码的话,可以看到MethodAccessorGenerator是如何一点点把Java版的MethodAccessor实现类生产出来的。也可以看到GeneratedMethodAccessor+数字这种名字是从哪里来的了,就在上面的generateName()方法里。
对本文开头的例子的A.foo(),生成的Java版MethodAccessor大致如下:
就反射调用而言,这个invoke()方法非常干净(然而就“正常调用”而言这额外开销还是明显的)。注意到参数数组被拆开了,把每个参数都恢复到原本没有被Object[]包装前的样子,然后对目标方法做正常的invokevirtual调用。由于在生成代码时已经循环遍历过参数类型的数组,生成出来的代码里就不再包含循环了。
当该反射调用成为热点时,它甚至可以被内联到靠近Method.invoke()的一侧,大大降低了反射调用的开销。而native版的反射调用则无法被有效内联,因而调用开销无法随程序的运行而降低。
虽说Sun的JDK这种实现方式使得反射调用方法成本比以前降低了很多,但Method.invoke()本身要用数组包装参数;而且每次调用都必须检查方法的可见性(在Method.invoke()里),也必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0()里或者生成的Java版MethodAccessor.invoke()里);而且Method.invoke()就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得Method.invoke()自身难以被内联到调用方。
相比之下JDK 7里新的MethodHandle则更有潜力,在其功能完全实现后能达到比普通反射调用方法更高的性能。在使用MethodHandle来做反射调用时,MethodHandle.invoke()的形式参数与返回值类型都是准确的,所以只需要在链接方法的时候才需要检查类型的匹配性,而不必在每次调用时都检查。而且MethodHandle是不可变值,在创建后其内部状态就不会再改变了;JVM可以利用这个知识而放心的对它做激进优化,例如将实际的调用目标内联到做反射调用的一侧。
到本来Java的安全机制使得不同类之间不是任意信息都可见,但Sun的JDK里开了个口,有一个标记类专门用于开后门:
那个"__JVM_DefineClass__"的来源是这里:
src/share/vm/prims/jvm.cpp
OK,本文就记到这里吧。希望对问答频道提问那位同学有帮助。
Have fun ^_^
============================================================================
P.S. log里的"shared objects file"其实也是个有趣的话题。有机会的话也可以写写。
orz...还有这种事。请问有stack trace么?
呵呵呵呵,仔细点阅读代码的话就不会有这个疑问了。
来,看这里:
sun.reflect.ReflectionFactory
在为ConstructorAccessorImpl的子类的构造器创建constructor accessor的时候,并不像一般情况创建出包着NativeConstructorAccessorImpl的DelegatingConstructorAccessorImpl,而是创建出一个BootstrapConstructorAccessorImpl,后者不会再创建新的constructor accessor,就破掉循环了
结合前面的例子,对A.foo()的反射调用做优化。
generate()方法只是先根据需要造出了名为GeneratedMethodAccessor1的类,然后调用该类的newInstance()等同于new GeneratedMethodAccessor1(),这里是反射调用了一个新的类的构造器,与原本的A.foo()并没有关系。
不知道什么地方看起来会有死循环呢?
里面的这段代码
请问java reflection到底是怎样work的?
蓝光满塞,BRS满塞~
liu0013 写道
[Loaded sun.reflect.GeneratedMethodAccessor197 from __JVM_DefineClass__]
请问报这个是什么意思?
请问报这个是什么意思?
这是Sun实现的Java标准库的一个细节。下面举例稍微讲解一下。
假如有这么一个类A:
public class A { public void foo(String name) { System.out.println("Hello, " + name); } }
可以编写另外一个类来反射调用A上的方法:
import java.lang.reflect.Method; public class TestClassLoad { public static void main(String[] args) throws Exception { Class<?> clz = Class.forName("A"); Object o = clz.newInstance(); Method m = clz.getMethod("foo", String.class); for (int i = 0; i < 16; i++) { m.invoke(o, Integer.toString(i)); } } }
注意到TestClassLoad类上不会有对类A的符号依赖——也就是说在加载并初始化TestClassLoad类时不需要关心类A的存在与否,而是等到main()方法执行到调用Class.forName()时才试图对类A做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的ClassLoader来加载,并且初始化新加载的类。……好吧这个细节跟主题没啥关系。
回到主题。这次我的测试环境是Sun的JDK 1.6.0 update 13 build 03。编译上述代码,并在执行TestClassLoad时加入-XX:+TraceClassLoading参数(或者-verbose:class或者直接-verbose都行),如下:
java -XX:+TraceClassLoading TestClassLoad
可以看到输出了一大堆log,把其中相关的部分截取出来如下:(完整的log可以从附件下载)
[Loaded TestClassLoad from file:/D:/temp_code/test_java_classload/] [Loaded A from file:/D:/temp_code/test_java_classload/] [Loaded sun.reflect.NativeMethodAccessorImpl from shared objects file] [Loaded sun.reflect.DelegatingMethodAccessorImpl from shared objects file] Hello, 0 Hello, 1 Hello, 2 Hello, 3 Hello, 4 Hello, 5 Hello, 6 Hello, 7 Hello, 8 Hello, 9 Hello, 10 Hello, 11 Hello, 12 Hello, 13 Hello, 14 [Loaded sun.reflect.ClassFileConstants from shared objects file] [Loaded sun.reflect.AccessorGenerator from shared objects file] [Loaded sun.reflect.MethodAccessorGenerator from shared objects file] [Loaded sun.reflect.ByteVectorFactory from shared objects file] [Loaded sun.reflect.ByteVector from shared objects file] [Loaded sun.reflect.ByteVectorImpl from shared objects file] [Loaded sun.reflect.ClassFileAssembler from shared objects file] [Loaded sun.reflect.UTF8 from shared objects file] [Loaded java.lang.Void from shared objects file] [Loaded sun.reflect.Label from shared objects file] [Loaded sun.reflect.Label$PatchInfo from shared objects file] [Loaded java.util.AbstractList$Itr from shared objects file] [Loaded sun.reflect.MethodAccessorGenerator$1 from shared objects file] [Loaded sun.reflect.ClassDefiner from shared objects file] [Loaded sun.reflect.ClassDefiner$1 from shared objects file] [Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__] Hello, 15
可以看到前15次反射调用A.foo()方法并没有什么稀奇的地方,但在第16次反射调用时似乎有什么东西被触发了,导致JVM新加载了一堆类,其中就包括[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]这么一行。这是哪里来的呢?
先来看看JDK里Method.invoke()是怎么实现的。
java.lang.reflect.Method:
public final class Method extends AccessibleObject implements GenericDeclaration, Member { // ... private volatile MethodAccessor methodAccessor; // For sharing of MethodAccessors. This branching structure is // currently only two levels deep (i.e., one root Method and // potentially many Method objects pointing to it.) private Method root; // ... public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class caller = Reflection.getCallerClass(1); Class targetClass = ((obj == null || !Modifier.isProtected(modifiers)) ? clazz : obj.getClass()); boolean cached; synchronized (this) { cached = (securityCheckCache == caller) && (securityCheckTargetClassCache == targetClass); } if (!cached) { Reflection.ensureMemberAccess(caller, clazz, obj, modifiers); synchronized (this) { securityCheckCache = caller; securityCheckTargetClassCache = targetClass; } } } } if (methodAccessor == null) acquireMethodAccessor(); return methodAccessor.invoke(obj, args); } // NOTE that there is no synchronization used here. It is correct // (though not efficient) to generate more than one MethodAccessor // for a given Method. However, avoiding synchronization will // probably make the implementation more scalable. private void acquireMethodAccessor() { // First check to see if one has been created yet, and take it // if so MethodAccessor tmp = null; if (root != null) tmp = root.getMethodAccessor(); if (tmp != null) { methodAccessor = tmp; return; } // Otherwise fabricate one and propagate it up to the root tmp = reflectionFactory.newMethodAccessor(this); setMethodAccessor(tmp); } // ... }
可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。
每个实际的Java方法只有一个对应的Method对象作为root,。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。在第一次调用一个实际Java方法对应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。
那么MethodAccessor是啥呢?
sun.reflect.MethodAccessor:
public interface MethodAccessor { /** Matches specification in {@link java.lang.reflect.Method} */ public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException; }
可以看到它只是一个单方法接口,其invoke()方法与Method.invoke()的对应。
创建MethodAccessor实例的是ReflectionFactory。
sun.reflect.ReflectionFactory:
public class ReflectionFactory { private static boolean initted = false; // ... // // "Inflation" mechanism. Loading bytecodes to implement // Method.invoke() and Constructor.newInstance() currently costs // 3-4x more than an invocation via native code for the first // invocation (though subsequent invocations have been benchmarked // to be over 20x faster). Unfortunately this cost increases // startup time for certain applications that use reflection // intensively (but only once per class) to bootstrap themselves. // To avoid this penalty we reuse the existing JVM entry points // for the first few invocations of Methods and Constructors and // then switch to the bytecode-based implementations. // // Package-private to be accessible to NativeMethodAccessorImpl // and NativeConstructorAccessorImpl private static boolean noInflation = false; private static int inflationThreshold = 15; // ... /** We have to defer full initialization of this class until after the static initializer is run since java.lang.reflect.Method's static initializer (more properly, that for java.lang.reflect.AccessibleObject) causes this class's to be run, before the system properties are set up. */ private static void checkInitted() { if (initted) return; AccessController.doPrivileged(new PrivilegedAction() { public Object run() { // Tests to ensure the system properties table is fully // initialized. This is needed because reflection code is // called very early in the initialization process (before // command-line arguments have been parsed and therefore // these user-settable properties installed.) We assume that // if System.out is non-null then the System class has been // fully initialized and that the bulk of the startup code // has been run. if (System.out == null) { // java.lang.System not yet fully initialized return null; } String val = System.getProperty("sun.reflect.noInflation"); if (val != null && val.equals("true")) { noInflation = true; } val = System.getProperty("sun.reflect.inflationThreshold"); if (val != null) { try { inflationThreshold = Integer.parseInt(val); } catch (NumberFormatException e) { throw (RuntimeException) new RuntimeException("Unable to parse property sun.reflect.inflationThreshold"). initCause(e); } } initted = true; return null; } }); } // ... public MethodAccessor newMethodAccessor(Method method) { checkInitted(); if (noInflation) { return new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers()); } else { NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method); DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc); acc.setParent(res); return res; } } }
这里就可以看到有趣的地方了。如注释所述,实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。
Sun的JDK是从1.4系开始采用这种优化的,主要作者是Ken Russell
上面看到了ReflectionFactory.newMethodAccessor()生产MethodAccessor的逻辑,在“开头若干次”时用到的DelegatingMethodAccessorImpl代码如下:
sun.reflect.DelegatingMethodAccessorImpl:
/** Delegates its invocation to another MethodAccessorImpl and can change its delegate at run time. */ class DelegatingMethodAccessorImpl extends MethodAccessorImpl { private MethodAccessorImpl delegate; DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) { setDelegate(delegate); } public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { return delegate.invoke(obj, args); } void setDelegate(MethodAccessorImpl delegate) { this.delegate = delegate; } }
这是一个间接层,方便在native与Java版的MethodAccessor之间实现切换。
然后下面就是native版MethodAccessor的Java一侧的声明:
sun.reflect.NativeMethodAccessorImpl:
/** Used only for the first few invocations of a Method; afterward, switches to bytecode-based implementation */ class NativeMethodAccessorImpl extends MethodAccessorImpl { private Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; NativeMethodAccessorImpl(Method method) { this.method = method; } public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { if (++numInvocations > ReflectionFactory.inflationThreshold()) { MethodAccessorImpl acc = (MethodAccessorImpl) new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers()); parent.setDelegate(acc); } return invoke0(method, obj, args); } void setParent(DelegatingMethodAccessorImpl parent) { this.parent = parent; } private static native Object invoke0(Method m, Object obj, Object[] args); }
每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。
注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的:
JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0 (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args) { return JVM_InvokeMethod(env, m, obj, args); }
JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0)) JVMWrapper("JVM_InvokeMethod"); Handle method_handle; if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) { method_handle = Handle(THREAD, JNIHandles::resolve(method)); Handle receiver(THREAD, JNIHandles::resolve(obj)); objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0))); oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL); jobject res = JNIHandles::make_local(env, result); if (JvmtiExport::should_post_vm_object_alloc()) { oop ret_type = java_lang_reflect_Method::return_type(method_handle()); assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!"); if (java_lang_Class::is_primitive(ret_type)) { // Only for primitive type vm allocates memory for java object. // See box() method. JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } } return res; } else { THROW_0(vmSymbols::java_lang_StackOverflowError()); } JVM_END
其中的关键又是Reflection::invoke_method():
// This would be nicer if, say, java.lang.reflect.Method was a subclass // of java.lang.reflect.Constructor oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) { oop mirror = java_lang_reflect_Method::clazz(method_mirror); int slot = java_lang_reflect_Method::slot(method_mirror); bool override = java_lang_reflect_Method::override(method_mirror) != 0; objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror))); oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror); BasicType rtype; if (java_lang_Class::is_primitive(return_type_mirror)) { rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL); } else { rtype = T_OBJECT; } instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(mirror)); methodOop m = klass->method_with_idnum(slot); if (m == NULL) { THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke"); } methodHandle method(THREAD, m); return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD); }
再下去就深入到HotSpot VM的内部了,本文就在这里打住吧。有同学有兴趣深究的话以后可以再写一篇讨论native版的实现。
回到Java的一侧。MethodAccessorGenerator长啥样呢?由于代码太长,这里就不完整贴了,有兴趣的可以到OpenJDK 6的Mercurial仓库看:OpenJDK 6 build 17的MethodAccessorGenerator。它的基本工作就是在内存里生成新的专用Java类,并将其加载。就贴这么一个方法:
private static synchronized String generateName(boolean isConstructor, boolean forSerialization) { if (isConstructor) { if (forSerialization) { int num = ++serializationConstructorSymnum; return "sun/reflect/GeneratedSerializationConstructorAccessor" + num; } else { int num = ++constructorSymnum; return "sun/reflect/GeneratedConstructorAccessor" + num; } } else { int num = ++methodSymnum; return "sun/reflect/GeneratedMethodAccessor" + num; } }
去阅读源码的话,可以看到MethodAccessorGenerator是如何一点点把Java版的MethodAccessor实现类生产出来的。也可以看到GeneratedMethodAccessor+数字这种名字是从哪里来的了,就在上面的generateName()方法里。
对本文开头的例子的A.foo(),生成的Java版MethodAccessor大致如下:
package sun.reflect; public class GeneratedMethodAccessor1 extends MethodAccessorImpl { public GeneratedMethodAccessor1() { super(); } public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { // prepare the target and parameters if (obj == null) throw new NullPointerException(); try { A target = (A) obj; if (args.length != 1) throw new IllegalArgumentException(); String arg0 = (String) args[0]; } catch (ClassCastException e) { throw new IllegalArgumentException(e.toString()); } catch (NullPointerException e) { throw new IllegalArgumentException(e.toString()); } // make the invocation try { target.foo(arg0); } catch (Throwable t) { throw new InvocationTargetException(t); } } }
就反射调用而言,这个invoke()方法非常干净(然而就“正常调用”而言这额外开销还是明显的)。注意到参数数组被拆开了,把每个参数都恢复到原本没有被Object[]包装前的样子,然后对目标方法做正常的invokevirtual调用。由于在生成代码时已经循环遍历过参数类型的数组,生成出来的代码里就不再包含循环了。
当该反射调用成为热点时,它甚至可以被内联到靠近Method.invoke()的一侧,大大降低了反射调用的开销。而native版的反射调用则无法被有效内联,因而调用开销无法随程序的运行而降低。
虽说Sun的JDK这种实现方式使得反射调用方法成本比以前降低了很多,但Method.invoke()本身要用数组包装参数;而且每次调用都必须检查方法的可见性(在Method.invoke()里),也必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0()里或者生成的Java版MethodAccessor.invoke()里);而且Method.invoke()就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得Method.invoke()自身难以被内联到调用方。
相比之下JDK 7里新的MethodHandle则更有潜力,在其功能完全实现后能达到比普通反射调用方法更高的性能。在使用MethodHandle来做反射调用时,MethodHandle.invoke()的形式参数与返回值类型都是准确的,所以只需要在链接方法的时候才需要检查类型的匹配性,而不必在每次调用时都检查。而且MethodHandle是不可变值,在创建后其内部状态就不会再改变了;JVM可以利用这个知识而放心的对它做激进优化,例如将实际的调用目标内联到做反射调用的一侧。
到本来Java的安全机制使得不同类之间不是任意信息都可见,但Sun的JDK里开了个口,有一个标记类专门用于开后门:
package sun.reflect; /** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl and others, not because it actually implements an interface) is a marker class in the hierarchy. All subclasses of this class are "magically" granted access by the VM to otherwise inaccessible fields and methods of other classes. It is used to hold the code for dynamically-generated FieldAccessorImpl and MethodAccessorImpl subclasses. (Use of the word "unsafe" was avoided in this class's name to avoid confusion with {@link sun.misc.Unsafe}.) </P> <P> The bug fix for 4486457 also necessitated disabling verification for this class and all subclasses, as opposed to just SerializationConstructorAccessorImpl and subclasses, to avoid having to indicate to the VM which of these dynamically-generated stub classes were known to be able to pass the verifier. </P> <P> Do not change the name of this class without also changing the VM's code. </P> */ class MagicAccessorImpl { }
那个"__JVM_DefineClass__"的来源是这里:
src/share/vm/prims/jvm.cpp
// common code for JVM_DefineClass() and JVM_DefineClassWithSource() // and JVM_DefineClassWithSourceCond() static jclass jvm_define_class_common(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source, jboolean verify, TRAPS) { if (source == NULL) source = "__JVM_DefineClass__";
OK,本文就记到这里吧。希望对问答频道提问那位同学有帮助。
Have fun ^_^
============================================================================
P.S. log里的"shared objects file"其实也是个有趣的话题。有机会的话也可以写写。
- test_java_classload.zip (3.2 KB)
- 下载次数: 67
评论
15 楼
ljh8088
2014-11-24
最近在尝试反射优化,又涨姿势了。
14 楼
bluesleaf
2011-10-29
引用
P.S. log里的"shared objects file"其实也是个有趣的话题。有机会的话也可以写写。
求该话题~
13 楼
RednaxelaFX
2011-04-20
sky_sz 写道
我的项目中出现java.lang.ClassNotFoundException: sun.reflect.GeneratedMethodAccessor27为什么啊?
orz...还有这种事。请问有stack trace么?
12 楼
sky_sz
2011-04-20
我的项目中出现java.lang.ClassNotFoundException: sun.reflect.GeneratedMethodAccessor27为什么啊?
11 楼
raintung.li
2011-03-29
具体的反射为什么性能急剧下降,请看http://blog.csdn.net/raintungli/archive/2011/03/29/6286701.aspx
10 楼
buaasss
2010-09-10
嗯,现在知道了,感谢感谢:)
9 楼
RednaxelaFX
2010-09-06
buaasss 写道
constructorAccessor.newInstance(initargs);这行代码
继续调用了一些方法,最后进入了MethodAccessorGenerator类的generate方法,又走到了ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader()).newInstance()这一步,这难道不是一个死循环吗?
继续调用了一些方法,最后进入了MethodAccessorGenerator类的generate方法,又走到了ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader()).newInstance()这一步,这难道不是一个死循环吗?
呵呵呵呵,仔细点阅读代码的话就不会有这个疑问了。
来,看这里:
sun.reflect.ReflectionFactory
// Bootstrapping issue: since we use Class.newInstance() in // the ConstructorAccessor generation process, we have to // break the cycle here.
在为ConstructorAccessorImpl的子类的构造器创建constructor accessor的时候,并不像一般情况创建出包着NativeConstructorAccessorImpl的DelegatingConstructorAccessorImpl,而是创建出一个BootstrapConstructorAccessorImpl,后者不会再创建新的constructor accessor,就破掉循环了
8 楼
buaasss
2010-09-06
先谢谢你的解答!:) 但是我还是没有弄明白。
ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader())返回的是java.lang.Class类型的对象,它的newInstance()方法本质是调用java.lang.reflect.Constructor类中的public T newInstance(Object ... initargs)方法,源代码如下:
从中可以看到调用了constructorAccessor.newInstance(initargs)方法;
constructorAccessor是从acquireConstructorAccessor()得到的
constructorAccessor.newInstance(initargs);这行代码
继续调用了一些方法,最后进入了MethodAccessorGenerator类的generate方法,又走到了ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader()).newInstance()这一步,这难道不是一个死循环吗?
ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader())返回的是java.lang.Class类型的对象,它的newInstance()方法本质是调用java.lang.reflect.Constructor类中的public T newInstance(Object ... initargs)方法,源代码如下:
public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class caller = Reflection.getCallerClass(2); if (securityCheckCache != caller) { Reflection.ensureMemberAccess(caller, clazz, null, modifiers); securityCheckCache = caller; } } } if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); if (constructorAccessor == null) acquireConstructorAccessor(); return (T) constructorAccessor.newInstance(initargs); }
从中可以看到调用了constructorAccessor.newInstance(initargs)方法;
constructorAccessor是从acquireConstructorAccessor()得到的
private void acquireConstructorAccessor() { // First check to see if one has been created yet, and take it // if so. ConstructorAccessor tmp = null; if (root != null) tmp = root.getConstructorAccessor(); if (tmp != null) { constructorAccessor = tmp; return; } // Otherwise fabricate one and propagate it up to the root tmp = reflectionFactory.newConstructorAccessor(this); setConstructorAccessor(tmp); }
constructorAccessor.newInstance(initargs);这行代码
继续调用了一些方法,最后进入了MethodAccessorGenerator类的generate方法,又走到了ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader()).newInstance()这一步,这难道不是一个死循环吗?
7 楼
RednaxelaFX
2010-09-02
buaasss 写道
里面的这段代码
请问java reflection到底是怎样work的?
ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader()).newInstance();又用newInstance()这个方法,这不是会导致死循环?
请问java reflection到底是怎样work的?
结合前面的例子,对A.foo()的反射调用做优化。
generate()方法只是先根据需要造出了名为GeneratedMethodAccessor1的类,然后调用该类的newInstance()等同于new GeneratedMethodAccessor1(),这里是反射调用了一个新的类的构造器,与原本的A.foo()并没有关系。
不知道什么地方看起来会有死循环呢?
6 楼
buaasss
2010-08-29
private MagicAccessorImpl generate(final Class declaringClass, 127 String name, 128 Class[] parameterTypes, 129 Class returnType, 130 Class[] checkedExceptions, 131 int modifiers, 132 boolean isConstructor, 133 boolean forSerialization, 134 Class serializationTargetClass) 135 { 136 ByteVector vec = ByteVectorFactory.create(); 137 asm = new ClassFileAssembler(vec); 138 this.declaringClass = declaringClass; 139 this.parameterTypes = parameterTypes; 140 this.returnType = returnType; 141 this.modifiers = modifiers; 142 this.isConstructor = isConstructor; 143 this.forSerialization = forSerialization; 144 145 asm.emitMagicAndVersion(); 146 147 // Constant pool entries: 148 // ( * = Boxing information: optional) 149 // (+ = Shared entries provided by AccessorGenerator) 150 // (^ = Only present if generating SerializationConstructorAccessor) 151 // [UTF-8] [This class's name] 152 // [CONSTANT_Class_info] for above 153 // [UTF-8] "sun/reflect/{MethodAccessorImpl,ConstructorAccessorImpl,SerializationConstructorAccessorImpl}" 154 // [CONSTANT_Class_info] for above 155 // [UTF-8] [Target class's name] 156 // [CONSTANT_Class_info] for above 157 // ^ [UTF-8] [Serialization: Class's name in which to invoke constructor] 158 // ^ [CONSTANT_Class_info] for above 159 // [UTF-8] target method or constructor name 160 // [UTF-8] target method or constructor signature 161 // [CONSTANT_NameAndType_info] for above 162 // [CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info] for target method 163 // [UTF-8] "invoke" or "newInstance" 164 // [UTF-8] invoke or newInstance descriptor 165 // [UTF-8] descriptor for type of non-primitive parameter 1 166 // [CONSTANT_Class_info] for type of non-primitive parameter 1 167 // ... 168 // [UTF-8] descriptor for type of non-primitive parameter n 169 // [CONSTANT_Class_info] for type of non-primitive parameter n 170 // + [UTF-8] "java/lang/Exception" 171 // + [CONSTANT_Class_info] for above 172 // + [UTF-8] "java/lang/ClassCastException" 173 // + [CONSTANT_Class_info] for above 174 // + [UTF-8] "java/lang/NullPointerException" 175 // + [CONSTANT_Class_info] for above 176 // + [UTF-8] "java/lang/IllegalArgumentException" 177 // + [CONSTANT_Class_info] for above 178 // + [UTF-8] "java/lang/InvocationTargetException" 179 // + [CONSTANT_Class_info] for above 180 // + [UTF-8] "<init>" 181 // + [UTF-8] "()V" 182 // + [CONSTANT_NameAndType_info] for above 183 // + [CONSTANT_Methodref_info] for NullPointerException's constructor 184 // + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor 185 // + [UTF-8] "(Ljava/lang/String;)V" 186 // + [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/String;)V" 187 // + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor taking a String 188 // + [UTF-8] "(Ljava/lang/Throwable;)V" 189 // + [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/Throwable;)V" 190 // + [CONSTANT_Methodref_info] for InvocationTargetException's constructor 191 // + [CONSTANT_Methodref_info] for "super()" 192 // + [UTF-8] "java/lang/Object" 193 // + [CONSTANT_Class_info] for above 194 // + [UTF-8] "toString" 195 // + [UTF-8] "()Ljava/lang/String;" 196 // + [CONSTANT_NameAndType_info] for "toString()Ljava/lang/String;" 197 // + [CONSTANT_Methodref_info] for Object's toString method 198 // + [UTF-8] "Code" 199 // + [UTF-8] "Exceptions" 200 // * [UTF-8] "java/lang/Boolean" 201 // * [CONSTANT_Class_info] for above 202 // * [UTF-8] "(Z)V" 203 // * [CONSTANT_NameAndType_info] for above 204 // * [CONSTANT_Methodref_info] for above 205 // * [UTF-8] "booleanValue" 206 // * [UTF-8] "()Z" 207 // * [CONSTANT_NameAndType_info] for above 208 // * [CONSTANT_Methodref_info] for above 209 // * [UTF-8] "java/lang/Byte" 210 // * [CONSTANT_Class_info] for above 211 // * [UTF-8] "(B)V" 212 // * [CONSTANT_NameAndType_info] for above 213 // * [CONSTANT_Methodref_info] for above 214 // * [UTF-8] "byteValue" 215 // * [UTF-8] "()B" 216 // * [CONSTANT_NameAndType_info] for above 217 // * [CONSTANT_Methodref_info] for above 218 // * [UTF-8] "java/lang/Character" 219 // * [CONSTANT_Class_info] for above 220 // * [UTF-8] "(C)V" 221 // * [CONSTANT_NameAndType_info] for above 222 // * [CONSTANT_Methodref_info] for above 223 // * [UTF-8] "charValue" 224 // * [UTF-8] "()C" 225 // * [CONSTANT_NameAndType_info] for above 226 // * [CONSTANT_Methodref_info] for above 227 // * [UTF-8] "java/lang/Double" 228 // * [CONSTANT_Class_info] for above 229 // * [UTF-8] "(D)V" 230 // * [CONSTANT_NameAndType_info] for above 231 // * [CONSTANT_Methodref_info] for above 232 // * [UTF-8] "doubleValue" 233 // * [UTF-8] "()D" 234 // * [CONSTANT_NameAndType_info] for above 235 // * [CONSTANT_Methodref_info] for above 236 // * [UTF-8] "java/lang/Float" 237 // * [CONSTANT_Class_info] for above 238 // * [UTF-8] "(F)V" 239 // * [CONSTANT_NameAndType_info] for above 240 // * [CONSTANT_Methodref_info] for above 241 // * [UTF-8] "floatValue" 242 // * [UTF-8] "()F" 243 // * [CONSTANT_NameAndType_info] for above 244 // * [CONSTANT_Methodref_info] for above 245 // * [UTF-8] "java/lang/Integer" 246 // * [CONSTANT_Class_info] for above 247 // * [UTF-8] "(I)V" 248 // * [CONSTANT_NameAndType_info] for above 249 // * [CONSTANT_Methodref_info] for above 250 // * [UTF-8] "intValue" 251 // * [UTF-8] "()I" 252 // * [CONSTANT_NameAndType_info] for above 253 // * [CONSTANT_Methodref_info] for above 254 // * [UTF-8] "java/lang/Long" 255 // * [CONSTANT_Class_info] for above 256 // * [UTF-8] "(J)V" 257 // * [CONSTANT_NameAndType_info] for above 258 // * [CONSTANT_Methodref_info] for above 259 // * [UTF-8] "longValue" 260 // * [UTF-8] "()J" 261 // * [CONSTANT_NameAndType_info] for above 262 // * [CONSTANT_Methodref_info] for above 263 // * [UTF-8] "java/lang/Short" 264 // * [CONSTANT_Class_info] for above 265 // * [UTF-8] "(S)V" 266 // * [CONSTANT_NameAndType_info] for above 267 // * [CONSTANT_Methodref_info] for above 268 // * [UTF-8] "shortValue" 269 // * [UTF-8] "()S" 270 // * [CONSTANT_NameAndType_info] for above 271 // * [CONSTANT_Methodref_info] for above 272 273 short numCPEntries = NUM_BASE_CPOOL_ENTRIES + NUM_COMMON_CPOOL_ENTRIES; 274 boolean usesPrimitives = usesPrimitiveTypes(); 275 if (usesPrimitives) { 276 numCPEntries += NUM_BOXING_CPOOL_ENTRIES; 277 } 278 if (forSerialization) { 279 numCPEntries += NUM_SERIALIZATION_CPOOL_ENTRIES; 280 } 281 282 // Add in variable-length number of entries to be able to describe 283 // non-primitive parameter types and checked exceptions. 284 numCPEntries += (short) (2 * numNonPrimitiveParameterTypes()); 285 286 asm.emitShort(add(numCPEntries, S1)); 287 288 final String generatedName = generateName(isConstructor, forSerialization); 289 asm.emitConstantPoolUTF8(generatedName); 290 asm.emitConstantPoolClass(asm.cpi()); 291 thisClass = asm.cpi(); 292 if (isConstructor) { 293 if (forSerialization) { 294 asm.emitConstantPoolUTF8 295 ("sun/reflect/SerializationConstructorAccessorImpl"); 296 } else { 297 asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl"); 298 } 299 } else { 300 asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl"); 301 } 302 asm.emitConstantPoolClass(asm.cpi()); 303 superClass = asm.cpi(); 304 asm.emitConstantPoolUTF8(getClassName(declaringClass, false)); 305 asm.emitConstantPoolClass(asm.cpi()); 306 targetClass = asm.cpi(); 307 short serializationTargetClassIdx = (short) 0; 308 if (forSerialization) { 309 asm.emitConstantPoolUTF8(getClassName(serializationTargetClass, false)); 310 asm.emitConstantPoolClass(asm.cpi()); 311 serializationTargetClassIdx = asm.cpi(); 312 } 313 asm.emitConstantPoolUTF8(name); 314 asm.emitConstantPoolUTF8(buildInternalSignature()); 315 asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); 316 if (isInterface()) { 317 asm.emitConstantPoolInterfaceMethodref(targetClass, asm.cpi()); 318 } else { 319 if (forSerialization) { 320 asm.emitConstantPoolMethodref(serializationTargetClassIdx, asm.cpi()); 321 } else { 322 asm.emitConstantPoolMethodref(targetClass, asm.cpi()); 323 } 324 } 325 targetMethodRef = asm.cpi(); 326 if (isConstructor) { 327 asm.emitConstantPoolUTF8("newInstance"); 328 } else { 329 asm.emitConstantPoolUTF8("invoke"); 330 } 331 invokeIdx = asm.cpi(); 332 if (isConstructor) { 333 asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;"); 334 } else { 335 asm.emitConstantPoolUTF8 336 ("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); 337 } 338 invokeDescriptorIdx = asm.cpi(); 339 340 // Output class information for non-primitive parameter types 341 nonPrimitiveParametersBaseIdx = add(asm.cpi(), S2); 342 for (int i = 0; i < parameterTypes.length; i++) { 343 Class c = parameterTypes[i]; 344 if (!isPrimitive(c)) { 345 asm.emitConstantPoolUTF8(getClassName(c, false)); 346 asm.emitConstantPoolClass(asm.cpi()); 347 } 348 } 349 350 // Entries common to FieldAccessor, MethodAccessor and ConstructorAccessor 351 emitCommonConstantPoolEntries(); 352 353 // Boxing entries 354 if (usesPrimitives) { 355 emitBoxingContantPoolEntries(); 356 } 357 358 if (asm.cpi() != numCPEntries) { 359 throw new InternalError("Adjust this code (cpi = " + asm.cpi() + 360 ", numCPEntries = " + numCPEntries + ")"); 361 } 362 363 // Access flags 364 asm.emitShort(ACC_PUBLIC); 365 366 // This class 367 asm.emitShort(thisClass); 368 369 // Superclass 370 asm.emitShort(superClass); 371 372 // Interfaces count and interfaces 373 asm.emitShort(S0); 374 375 // Fields count and fields 376 asm.emitShort(S0); 377 378 // Methods count and methods 379 asm.emitShort(NUM_METHODS); 380 381 emitConstructor(); 382 emitInvoke(); 383 384 // Additional attributes (none) 385 asm.emitShort(S0); 386 387 // Load class 388 vec.trim(); 389 final byte[] bytes = vec.getData(); 390 // Note: the class loader is the only thing that really matters 391 // here -- it's important to get the generated code into the 392 // same namespace as the target class. Since the generated code 393 // is privileged anyway, the protection domain probably doesn't 394 // matter. 395 return (MagicAccessorImpl) 396 AccessController.doPrivileged(new PrivilegedAction() { 397 public Object run() { 398 try { 399 return ClassDefiner.defineClass 400 (generatedName, 401 bytes, 402 0, 403 bytes.length, 404 declaringClass.getClassLoader()).newInstance(); 405 } catch (InstantiationException e) { 406 throw (InternalError) 407 new InternalError().initCause(e); 408 } catch (IllegalAccessException e) { 409 throw (InternalError) 410 new InternalError().initCause(e); 411 } 412 } 413 }); 414 }
里面的这段代码
ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader()).newInstance();又用newInstance()这个方法,这不是会导致死循环?
请问java reflection到底是怎样work的?
5 楼
ZZX19880809
2010-06-21
虽然看不太懂,但是还是有点收获的,好文章
4 楼
lgdlgd
2009-12-24
刚想了解一下关反射性能,就找到这了,好文章!
3 楼
iaimstar
2009-12-15
好吧
。。brs满赛,
。。brs满赛,
2 楼
RednaxelaFX
2009-12-15
iaimstar 写道
你的头像导致我看了半天一直以为进了ns的blog。。
蓝光满塞,BRS满塞~
1 楼
iaimstar
2009-12-15
你的头像导致我看了半天一直以为进了ns的blog。。
发表评论
-
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 16287以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10461先看看下面这个代码例子, 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 22405(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 21882之前有同学在做龙书(第二版)题目,做到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 48393刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ...
相关推荐
简单工厂模式通过一个中心工厂类来创建对象,工厂方法模式将对象的创建过程委托给子类决定,抽象工厂模式则关注于创建相关或依赖对象的家族,而不必指定它们的具体类。这些模式都提高了代码的可扩展性和可维护性。 ...
总的来说,通过反射调用获取Android的内置存储和外部SD卡的根路径是一种应对版本差异的有效手段,但同时也需要对设备的特定情况进行适配。在实际开发中,应遵循最佳实践,尽可能使用官方推荐的API,并确保在请求权...
反射是.NET Framework提供的一项功能,它允许运行时的程序获取关于自身类型、方法、属性等元数据的信息,并且能够动态地调用方法或创建对象。在C#中,我们通过`System.Reflection`命名空间下的类来实现反射。利用...
例如,我们可以创建一个`LogAttribute`,在方法上应用这个特性,以便在方法执行前后记录日志。下面是一个简单的示例: ```csharp [AttributeUsage(AttributeTargets.Method)] public class LogAttribute : ...
为了方便测试,我们可以在HTML页面中编写一个JavaScript函数来调用Java方法。例如,以下HTML文件展示了如何调用Java方法`showToast()`: ```html <!DOCTYPE html> ()">点击我 function callJavaMethod() { ...
本文将深入探讨反射的概念、工作原理以及如何在Android中使用反射,通过一个简单的示例进行讲解。 反射机制的核心是Java的`java.lang.Class`类,它代表了Java中的类和接口。通过`Class`对象,我们可以获取类的构造...
- `logUsingReflection()`:该方法通过反射调用`logInfo()`或`logError()`等方法。 - `Class.forName(className)`:根据类名获取对应的Class对象。 - `getDeclaredMethod()`:获取指定名称的方法。 - `getArgTypes()...
- 创建两个项目:一个是服务器端,提供一个可调用的方法;另一个是客户端,用于调用服务器端提供的方法。 - 服务器端: - 设计并实现一个服务接口及其实现类,提供一个特定的功能性方法。 - 使用Hadoop RPC框架...
在IT行业中,反射是一个至关重要的概念,特别是在编程语言如C#和JavaScript中。本文将深入探讨反射的概念,以及如何在ASP.NET环境中运用反射,并对比分析C#和JavaScript中的反射机制。 首先,我们要理解什么是反射...
本文将深入探讨如何通过反射获取`Context`的实例,并展示如何利用反射实现一个集成`Toast`的工具类。这在某些场景下非常有用,比如当我们需要在没有直接访问`Context`的情况下显示`Toast`消息。 首先,让我们了解`...
为了防止恶意JavaScript代码对Android应用造成危害,从API 17开始,`addJavascriptInterface()`默认禁用了反射调用。因此,我们需要使用`@JavascriptInterface`注解标记所有暴露给JavaScript的方法。此外,对于敏感...
每个Logger实例都是一个命名实体,可以通过名称调用,无需在不同类间传递。Logger具有继承体系,类似于.NET中的命名空间,子Logger继承父Logger的属性。 2. **Repository(库)**:Repository是存储和管理Logger的...
接着,我们调用`GetCustomAttributes`方法,传入自定义特性的类型和一个布尔值,表示是否从父类继承特性。最后,我们遍历获取到的特性实例,并打印出`CustomLogAttribute`中的`LogMessage`属性。 通过以上步骤,...
- `java.lang.reflect.InvocationHandler`: 定义了一个接口,用于处理代理对象的方法调用。 - `java.lang.reflect.Proxy`: 提供了创建代理对象的方法。 具体实现步骤如下: 1. **定义一个接口**:定义一个接口,让...
其次,反射在Java编程中是一种强大的工具,它允许程序在运行时检查类、接口、字段和方法的信息,甚至可以动态调用方法或改变对象状态。反射的应用场景包括:动态代理、元编程、插件系统等。虽然反射为程序提供了更大...
Log4j 是一个广泛使用的 Java 日志框架,由 Apache 软件基金会开发。它为应用程序提供了灵活的日志记录功能,允许开发者调整日志级别,定制日志格式,以及将日志输出到不同的目的地,如控制台、文件、数据库等。...
Java反射(Reflect)是Java语言中的一个重要特性,它允许运行时的Java程序访问、操作类、接口、字段和方法的信息,甚至动态调用方法。在Java编程中,反射提供了强大的能力,可以用来实现一些高级功能,如元编程、...