美文网首页Java
反射&动态代理

反射&动态代理

作者: 真的有神 | 来源:发表于2022-05-21 21:38 被阅读0次

    反射

    Java 的动态性体现在:反射机制、动态执行脚本语言、动态操作字节码

    反射:在运行时加载、探知、使用编译时未知的类。

    Class.forName 使用的类加载器是调用者的类加载器

    Class

    表示 Java 中的类型(class、interface、enum、annotation、primitive type、void)本身。

    一个类被加载之后,JVM 会创建一个对应该类的 Class 对象,类的整个结构信息会放在相应

    的 Class 对象中。

    这个 Class 对象就像一个镜子一样,从中可以看到类的所有信息。

    反射的核心就是 Class

    如果多次执行 forName 等加载类的方法,类只会被加载一次;一个类只会形成一个 Class

    对象,无论执行多少次加载类的方法,获得的 Class 都是一样的。

    用途

    性能

    反射带来灵活性的同时,也有降低程序执行效率的弊端

    setAccessible 方法不仅可以标记某些私有的属性方法为可访问的属性方法,并且可以提高程

    序的执行效率

    实际上是启用和禁用访问安全检查的开关。如果做检查就会降低效率;关闭检查就可以提高

    效率。

    反射调用方法比直接调用要慢大约 30 倍,如果跳过安全检查的话比直接调用要慢大约 7 倍

    开启和不开启安全检查对于反射而言可能会差 4 倍的执行效率。

    为什么慢?

    1)验证等防御代码过于繁琐,这一步本来在 link 阶段,现在却在计算时进行验证

    2)产生很多临时对象,造成 GC 与计算时间消耗

    3)由于缺少上下文,丢失了很多运行时的优化,比如 JIT(它可以看作 JVM 的重要评测标准

    之一)

    当然,现代 JVM 也不是非常慢了,它能够对反射代码进行缓存以及通过方法计数器同样实

    现 JIT 优化,所以反射不一定慢。

    实现

    反射在 Java 中可以直接调用,不过最终调用的仍是 native 方法,以下为主流反射操作的实

    现。

    Class.forName 的实现

    Class.forName 可以通过包名寻找 Class 对象,比如 Class.forName("java.lang.String")。

    在 JDK 的源码实现中,可以发现最终调用的是 native 方法 forName0(),它在 JVM 中调用的

    实际是 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 的实现

    在 JDK 源 码 中 , 可 以 知 道 class.getDeclaredFields() 方 法 实 际 调 用 的 是 native 方 法

    getDeclaredFields0(),它在 JVM 主要实现步骤如下:

    1)根据 Class 结构体信息,获取 field_count 与 fields[]字段,这个字段早已在 load 过程中被

    放入了

    2)根据 field_count 的大小分配内存、创建数组

    3)将数组进行 forEach 循环,通过 fields[]中的信息依次创建 Object 对象

    4)返回数组指针

    主要慢在如下方面:

    创建、计算、分配数组对象

    对字段进行循环赋值

    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 的实现

    以下为无同步、无异常的情况下调用的步骤

    1)创建 Frame

    2)如果对象 flag 为 native,交给 native_handler 进行处理

    3)在 frame 中执行 java 代码

    4)弹出 Frame

    5)返回执行结果的指针

    主要慢在如下方面:

    需要完全执行 ByteCode 而缺少 JIT 等优化

    检查参数非常多,这些本来可以在编译器或者加载时完成

    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 的实现

    1)检测权限、预分配空间大小等参数

    2)创建 Object 对象,并分配空间

    3)通过 Method.invoke 调用构造函数(<init>())

    4)返回 Object 指针

    主要慢在如下方面:

    参数检查不能优化或者遗漏

    <init>()的查表

    Method.invoke 本身耗时

    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;

    }

    }

    相关文章

      网友评论

        本文标题:反射&动态代理

        本文链接:https://www.haomeiwen.com/subject/bkwxprtx.html