[TOC]
文章转自:https://github.com/5A59/android-training/blob/master/jvm-art/java%E5%8F%8D%E5%B0%84%E4%B8%BA%E4%BB%80%E4%B9%88%E6%85%A2.md
我们在 Java 开发中,难免会接触到反射,而在一些框架中,反射的运用更是常见。我相信,每次提到反射,大家的第一反应一定是反射效率低,尽量少使用。 但是反射的效率到底低多少?反射效率低的原因在哪里? 这篇文章就来探索一下这些问题。 由于本机上安装的是 openjdk 12,所以这里就使用 openjdk 12 源码进行分析。
我们先看结论,然后分析一下 Java 反射的原理,过程中大家可以根据结论,对源码做一些思考,然后再根据原理中的一些实现,看看 Java 反射效率低的原因。
所以最终我们的结论是:
Java 反射效率低主要原因是:
Method#invoke 方法会对参数做封装和解封操作
需要检查方法可见性
需要校验参数
反射方法难以内联
JIT 无法优化
Java 反射原理–获取要反射的方法 1.1 反射的使用 我们先来看看 Java 反射使用的一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class RefTest { public static void main(String[] args) { try { Class clazz = Class.forName("com.zy.java.RefTest"); Object refTest = clazz.newInstance(); Method method = clazz.getDeclaredMethod("refMethod"); method.invoke(refTest); } catch (Exception e) { e.printStackTrace(); } } public void refMethod() { } }
我们在调用反射时,首先会创建 Class 对象,然后获取其 Method 对象,调用 invoke 方法。 获取反射方法时,有两个方法,getMethod 和 getDeclaredMethod,我们就从这两个方法开始,一步步看下反射的原理。 接下来就进入代码分析,大家做好准备。
1.2 getMethod / getDeclaredMethod 这里我们先整体看一下 getMethod 和 getDeclaredMethod 的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Class { @CallerSensitive public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { Objects.requireNonNull(name); SecurityManager sm = System.getSecurityManager(); if (sm != null) { // 1. 检查方法权限 checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true); } // 2. 获取方法 Method method = getMethod0(name, parameterTypes); if (method == null) { throw new NoSuchMethodException(methodToString(name, parameterTypes)); } // 3. 返回方法的拷贝 return getReflectionFactory().copyMethod(method); } @CallerSensitive public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { Objects.requireNonNull(name); SecurityManager sm = System.getSecurityManager(); if (sm != null) { // 1. 检查方法是权限 checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true); } // 2. 获取方法 Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); if (method == null) { throw new NoSuchMethodException(methodToString(name, parameterTypes)); } // 3. 返回方法的拷贝 return getReflectionFactory().copyMethod(method); } }
从上面的代码,我们可以看到,获取方法的流程分三步走:
检查方法权限
获取方法 Method 对象
返回方法的拷贝
这里主要有两个区别:
getMethod 中 checkMemberAccess 传入的是 Member.PUBLIC,而 getDeclaredMethod 传入的是 Member.DECLARED 这两个值有什么区别呢?我们看下代码中的注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 interface Member { /** * Identifies the set of all public members of a class or interface, * including inherited members. */ public static final int PUBLIC = 0; /** * Identifies the set of declared members of a class or interface. * Inherited members are not included. */ public static final int DECLARED = 1; }
注释里清楚的解释了 PUBLIC 和 DECLARED 的不同,PUBLIC 会包括所有的 public 方法,包括父类的方法,而 DECLARED 会包括所有自己定义的方法,public,protected,private 都在此,但是不包括父类的方法。 这也正是 getMethod 和 getDeclaredMethod 的区别。