反射
Java 的動(dòng)態(tài)性體現(xiàn)在:反射機(jī)制涵叮、動(dòng)態(tài)執(zhí)行腳本語(yǔ)言萌踱、動(dòng)態(tài)操作字節(jié)碼
反射:在運(yùn)行時(shí)加載厌漂、探知哨啃、使用編譯時(shí)未知的類(lèi)烧栋。
Class.forName 使用的類(lèi)加載器是調(diào)用者的類(lèi)加載器
Class
表示 Java 中的類(lèi)型(class、interface拳球、enum审姓、annotation、primitive type祝峻、void)本身魔吐。
一個(gè)類(lèi)被加載之后,JVM 會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)該類(lèi)的 Class 對(duì)象莱找,類(lèi)的整個(gè)結(jié)構(gòu)信息會(huì)放在相應(yīng)
的 Class 對(duì)象中酬姆。
這個(gè) Class 對(duì)象就像一個(gè)鏡子一樣,從中可以看到類(lèi)的所有信息奥溺。
反射的核心就是 Class
如果多次執(zhí)行 forName 等加載類(lèi)的方法轴踱,類(lèi)只會(huì)被加載一次;一個(gè)類(lèi)只會(huì)形成一個(gè) Class
對(duì)象谚赎,無(wú)論執(zhí)行多少次加載類(lèi)的方法,獲得的 Class 都是一樣的诱篷。
用途
性能
反射帶來(lái)靈活性的同時(shí)壶唤,也有降低程序執(zhí)行效率的弊端
setAccessible 方法不僅可以標(biāo)記某些私有的屬性方法為可訪問(wèn)的屬性方法,并且可以提高程
序的執(zhí)行效率
實(shí)際上是啟用和禁用訪問(wèn)安全檢查的開(kāi)關(guān)棕所。如果做檢查就會(huì)降低效率闸盔;關(guān)閉檢查就可以提高
效率。
反射調(diào)用方法比直接調(diào)用要慢大約 30 倍琳省,如果跳過(guò)安全檢查的話比直接調(diào)用要慢大約 7 倍
開(kāi)啟和不開(kāi)啟安全檢查對(duì)于反射而言可能會(huì)差 4 倍的執(zhí)行效率迎吵。
為什么慢?
1)驗(yàn)證等防御代碼過(guò)于繁瑣,這一步本來(lái)在 link 階段针贬,現(xiàn)在卻在計(jì)算時(shí)進(jìn)行驗(yàn)證
2)產(chǎn)生很多臨時(shí)對(duì)象击费,造成 GC 與計(jì)算時(shí)間消耗
3)由于缺少上下文,丟失了很多運(yùn)行時(shí)的優(yōu)化桦他,比如 JIT(它可以看作 JVM 的重要評(píng)測(cè)標(biāo)準(zhǔn)
之一)
當(dāng)然蔫巩,現(xiàn)代 JVM 也不是非常慢了,它能夠?qū)Ψ瓷浯a進(jìn)行緩存以及通過(guò)方法計(jì)數(shù)器同樣實(shí)
現(xiàn) JIT 優(yōu)化,所以反射不一定慢圆仔。
實(shí)現(xiàn)
反射在 Java 中可以直接調(diào)用垃瞧,不過(guò)最終調(diào)用的仍是 native 方法,以下為主流反射操作的實(shí)
現(xiàn)坪郭。
Class.forName 的實(shí)現(xiàn)
Class.forName 可以通過(guò)包名尋找 Class 對(duì)象个从,比如 Class.forName("java.lang.String")。
在 JDK 的源碼實(shí)現(xiàn)中歪沃,可以發(fā)現(xiàn)最終調(diào)用的是 native 方法 forName0()嗦锐,它在 JVM 中調(diào)用的
實(shí)際是 FindClassFromCaller(),原理與 ClassLoader 的流程一樣绸罗。
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller),
caller);
}
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
Java_java_lang_Class_forName0(JNIEnv *env, jclass this, jstring classname,
jboolean initialize, jobject loader, jclass caller)
{
char *clname;
jclass cls = 0;
char buf[128];
jsize len;
jsize unicode_len;
if (classname == NULL) {
JNU_ThrowNullPointerException(env, 0);
return 0;
}
len = (*env)->GetStringUTFLength(env, classname);
unicode_len = (*env)->GetStringLength(env, classname);
if (len >= (jsize)sizeof(buf)) {
clname = malloc(len + 1);
if (clname == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return NULL;
}
} else {
clname = buf;
}
(*env)->GetStringUTFRegion(env, classname, 0, unicode_len, clname);
if (VerifyFixClassname(clname) == JNI_TRUE) {
/* slashes present in clname, use name b4 translation for exception */
(*env)->GetStringUTFRegion(env, classname, 0, unicode_len, clname);
JNU_ThrowClassNotFoundException(env, clname);
goto done;
}
if (!VerifyClassname(clname, JNI_TRUE)) { /* expects slashed name */
JNU_ThrowClassNotFoundException(env, clname);
goto done;
}
cls = JVM_FindClassFromCaller(env, clname, initialize, loader, caller);
done:
if (clname != buf) {
free(clname);
}
return cls;
}
JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name,
jboolean init, jclass from))
JVMWrapper2("JVM_FindClassFromClass %s", name);
if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
// It's impossible to create this class; the name cannot fit
// into the constant pool.
THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
}
TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
oop from_class_oop = JNIHandles::resolve(from);
Klass* from_class = (from_class_oop == NULL)
? (Klass*)NULL
: java_lang_Class::as_Klass(from_class_oop);
oop class_loader = NULL;
oop protection_domain = NULL;
if (from_class != NULL) {
class_loader = from_class->class_loader();
protection_domain = from_class->protection_domain();
}
Handle h_loader(THREAD, class_loader);
Handle h_prot (THREAD, protection_domain);
jclass result = find_class_from_class_loader(env, h_name, init, h_loader,
h_prot, true, thread);
if (TraceClassResolution && result != NULL) {
// this function is generally only used for class loading during verification.
ResourceMark rm;
oop from_mirror = JNIHandles::resolve_non_null(from);
Klass* from_class = java_lang_Class::as_Klass(from_mirror);
const char * from_name = from_class->external_name();
oop mirror = JNIHandles::resolve_non_null(result);
Klass* to_class = java_lang_Class::as_Klass(mirror);
const char * to = to_class->external_name();
tty->print("RESOLVE %s %s (verification)\n", from_name, to);
}
return result;
JVM_END
getDeclaredFields 的實(shí)現(xiàn)
在 JDK 源 碼 中 意推, 可 以 知 道 class.getDeclaredFields() 方 法 實(shí) 際 調(diào) 用 的 是 native 方 法
getDeclaredFields0(),它在 JVM 主要實(shí)現(xiàn)步驟如下:
1)根據(jù) Class 結(jié)構(gòu)體信息珊蟀,獲取 field_count 與 fields[]字段菊值,這個(gè)字段早已在 load 過(guò)程中被
放入了
2)根據(jù) field_count 的大小分配內(nèi)存、創(chuàng)建數(shù)組
3)將數(shù)組進(jìn)行 forEach 循環(huán)育灸,通過(guò) fields[]中的信息依次創(chuàng)建 Object 對(duì)象
4)返回?cái)?shù)組指針
主要慢在如下方面:
創(chuàng)建腻窒、計(jì)算、分配數(shù)組對(duì)象
對(duì)字段進(jìn)行循環(huán)賦值
public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyFields(privateGetDeclaredFields(false));
}
private Field[] privateGetDeclaredFields(boolean publicOnly) {
checkInitted();
Field[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.declaredPublicFields : rd.declaredFields;
if (res != null) return res;
}
// No cached value available; request value from VM
res = Reflection.filterFields(this, getDeclaredFields0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicFields = res;
} else {
rd.declaredFields = res;
}
}
return res;
}
private static Field[] copyFields(Field[] arg) {
Field[] out = new Field[arg.length];
ReflectionFactory fact = getReflectionFactory();
for (int i = 0; i < arg.length; i++) {
out[i] = fact.copyField(arg[i]);
}
return out;
}
Method.invoke 的實(shí)現(xiàn)
以下為無(wú)同步磅崭、無(wú)異常的情況下調(diào)用的步驟
1)創(chuàng)建 Frame
2)如果對(duì)象 flag 為 native儿子,交給 native_handler 進(jìn)行處理
3)在 frame 中執(zhí)行 java 代碼
4)彈出 Frame
5)返回執(zhí)行結(jié)果的指針
主要慢在如下方面:
需要完全執(zhí)行 ByteCode 而缺少 JIT 等優(yōu)化
檢查參數(shù)非常多,這些本來(lái)可以在編譯器或者加載時(shí)完成
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
NativeMethodAccessorImpl#invoke
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
// We can't inflate methods belonging to vm-anonymous classes because
// that kind of class can't be referred to by name, hence can't be
// found from the generated bytecode.
if (++numInvocations > ReflectionFactory.inflationThreshold()
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
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); }
private static native Object invoke0(Method m, Object obj, Object[] args);
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
class.newInstance 的實(shí)現(xiàn)
1)檢測(cè)權(quán)限砸喻、預(yù)分配空間大小等參數(shù)
2)創(chuàng)建 Object 對(duì)象柔逼,并分配空間
3)通過(guò) Method.invoke 調(diào)用構(gòu)造函數(shù)(<init>())
4)返回 Object 指針
主要慢在如下方面:
參數(shù)檢查不能優(yōu)化或者遺漏
<init>()的查表
Method.invoke 本身耗時(shí)
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
if (cachedConstructor == null) {
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}