隨筆 - 4, 文章 - 0, 評論 - 2, 引用 - 0
          數據加載中……

          Groovy深入探索——DGM調用優化

          DGM調用優化是通過直接調用代替反射調用的方式來提高DGM方法的調用效率。

          注:以下分析的Groovy源代碼來自Groovy 1.8.0 rc4。

          DGM
          DGM其實是Groovy社區對DefaultGroovyMethods的簡稱,完整類名是org.codehaus.groovy.runtime.DefaultGroovyMethods。
          DefaultGroovyMethods類中包含了Groovy為JDK的類添加的各種方法,如
          [123].each { println it }
          中的each方法就是Groovy為Object類添加的方法,用于遍歷對象中的所有元素。在Object類的實例上調用each方法,其實調用的是DefaultGroovyMethods類中的
          public static <T> T each(T self, Closure closure)

          反射調用和直接調用
          在Groovy中,用于表示一個方法的是一個MetaMethod(這是一個抽象類)實例,MetaMethod類似于JDK中的Method。而在大多數情況下,這個表示方法的實例會是CachedMethod(MetaMethod的派生類)類型的。調用其代表的方法時,會調用CachedMethod的invoke方法,其代碼如下
           1 public final Object invoke(Object object, Object[] arguments) {
           2     try {
           3         return cachedMethod.invoke(object, arguments); // cachedMethod的類型是Method
           4     } catch (IllegalArgumentException e) {
           5         throw new InvokerInvocationException(e);
           6     } catch (IllegalAccessException e) {
           7         throw new InvokerInvocationException(e);
           8     } catch (InvocationTargetException e) {
           9         Throwable cause = e.getCause(); 
          10         throw (cause instanceof RuntimeException && !(cause instanceof MissingMethodException)) ? (RuntimeException) cause : new InvokerInvocationException(e);
          11     }
          12 }

          可見,在Groovy中一般是通過反射的方式調用方法的。
          眾所周知,在Java中,通過反射來調用方法,比直接調用方法會慢上幾倍。因此,DGM調用優化的思想就是通過直接調用來代替反射調用。而要實現直接調用,則需要為DGM中的每個方法生成一個從MetaMethod派生的包裝類,該類的invoke方法將直接調用DGM中對應的方法。

          DGM調用優化
          DGM調用優化的大體流程是這樣的:

          1. 在編譯Groovy自身的Java代碼(不是用Groovy寫的代碼)之后,通過調用DgmConverter類的main方法,為DefaultGroovyMethods的每個方法生成一個包裝類,該類繼承GeneratedMetaMethod類,而GeneratedMetaMethod類則繼承MetaMethod類。該包裝類的類名類似于org.codehaus.groovy.runtime.dgm$123($后跟一個數),在Groovy分發的jar包中可以找到總共918個這樣的類,說明DGM中總共有918個方法。這些包裝類的代碼形式如下(以上面提到的each方法的包裝類為例):
          1 public class dgm$123 extends GeneratedMetaMethod {
          2     
          3     public Object invoke(Object object, Object[] arguments) {
          4         return DefaultGroovyMethods.each((Object) object, (Closure) arguments[0]); // 將各參數強制轉換為對應的類型后,直接調用DefaultGroovyMethods中對應的方法
          5     }
          6     
          7 }

          2. 通過調用GeneratedMetaMethod.DgmMethodRecord.saveDgmInfo方法,將哪個DGM方法對應哪個包裝類的信息寫入META-INF/dgminfo文件中

          3. 當運行Groovy程序的時候,在MetaClassRegistryImpl的實例初始化時,通過調用GeneratedMetaMethod.DgmMethodRecord.loadDgmInfo方法,從META-INF/dgminfo文件中讀取上一步寫入的信息。然后為所有包裝類創建GeneratedMetaMethod.Proxy實例作為其代理

          4. 在Groovy程序第一次調用DGM方法時,則由GeneratedMetaMethod.Proxy實例載入對應的包裝類并實例化,然后調用其invoke方法。這樣就實現了包裝類的延遲加載,在一定程度上加快了程序初始化加載的速度

          那為什么不為用戶寫的Groovy程序的每一個方法,在運行時動態的創建這樣直接調用的包裝類,來提高每個方法的調用效率呢?那是因為這樣會產生大量的類,由于每個類都是需要占用內存的,所以會對JVM用于存放類信息的PermGen內存區域造成壓力,容易產生OutOfMemoryError。而DGM方法是Groovy程序中大量被調用的方法(即熱點),而且數量只有不到1000個,因此適合為其生成包裝類。

          代碼分析
          先來看看DgmConverter是如何為DGM方法生成包裝類的:
           1 public static void main(String[] args) throws IOException, ClassNotFoundException {
           2     Class [] classes = new Class [] { // DGM方法事實上是分別位于這幾個類中,而不只是在DefaultGroovyMethods類中
           3         DefaultGroovyMethods.class,
           4         SwingGroovyMethods.class,
           5         SqlGroovyMethods.class,
           6         XmlGroovyMethods.class,
           7         EncodingGroovyMethods.class,
           8         DateGroovyMethods.class,
           9         ProcessGroovyMethods.class
          10     };
          11 
          12     List<CachedMethod> cachedMethodsList = new ArrayList<CachedMethod> ();
          13     for (Class aClass : classes) {
          14         Collections.addAll(cachedMethodsList, ReflectionCache.getCachedClass(aClass).getMethods()); // 獲取這些類中的所有方法
          15     }
          16     final CachedMethod[] cachedMethods = cachedMethodsList.toArray(new CachedMethod[cachedMethodsList.size()]);
          17 
          18     List<GeneratedMetaMethod.DgmMethodRecord> records = new ArrayList<GeneratedMetaMethod.DgmMethodRecord> ();
          19     for (int i = 0, cur = 0; i < cachedMethods.length; i++) {
          20         CachedMethod method = cachedMethods[i];
          21         if (!method.isStatic() || !method.isPublic()) // DGM方法必須是static和public的
          22           continue;
          23         if (method.getCachedMethod().getAnnotation(Deprecated.class!= null// 已過時的DGM方法不處理
          24             continue;
          25         if (method.getParameterTypes().length == 0// DGM方法的第一個參數表示該方法應添加到哪個類上,因此DGM方法至少有一個參數
          26           continue;
          27 
          28         final Class returnType = method.getReturnType();
          29         final String className = "org/codehaus/groovy/runtime/dgm$" + cur;
          30 
          31         // record記錄著DGM方法和包裝類的對應關系
          32         GeneratedMetaMethod.DgmMethodRecord record = new GeneratedMetaMethod.DgmMethodRecord();
          33         records.add(record);
          34         record.methodName = method.getName();
          35         record.returnType = method.getReturnType();
          36         record.parameters = method.getNativeParameterTypes();
          37         record.className  = className;
          38 
          39         // 創建一個繼承GeneratedMetaMethod的包裝類
          40         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
          41         cw.visit(V1_3,ACC_PUBLIC, className,null,"org/codehaus/groovy/reflection/GeneratedMetaMethod"null);
          42         createConstructor(cw);
          43         final String methodDescriptor = BytecodeHelper.getMethodDescriptor(returnType, method.getNativeParameterTypes());
          44         createInvokeMethod(method, cw, returnType, methodDescriptor); // 我們只關注invoke方法的生成
          45         createDoMethodInvokeMethod(method, cw, className, returnType, methodDescriptor);
          46         createIsValidMethodMethod(method, cw, className);
          47         cw.visitEnd();
          48 
          49         // 將包裝類寫入class文件
          50         final byte[] bytes = cw.toByteArray();
          51         final FileOutputStream fileOutputStream = new FileOutputStream("target/classes/" + className + ".class");
          52         fileOutputStream.write(bytes);
          53         fileOutputStream.flush();
          54         fileOutputStream.close();
          55 
          56         cur++;
          57     }
          58 
          59     GeneratedMetaMethod.DgmMethodRecord.saveDgmInfo (records, "target/classes/META-INF/dgminfo"); // 將DGM方法和包裝類的對應關系寫入META-INF/dgminfo文件
          60 }

          我們再來看看用于創建包裝類的invoke方法的createInvokeMethod方法:
           1 private static void createInvokeMethod(CachedMethod method, ClassWriter cw, Class returnType, String methodDescriptor) {
           2     MethodVisitor mv;
           3     mv = cw.visitMethod(ACC_PUBLIC, "invoke""(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"nullnull); // 創建一個public Object invoke(Object object, Object[] arguments)方法
           4     mv.visitCode();
           5     mv.visitVarInsn(ALOAD,1);
           6     BytecodeHelper.doCast(mv, method.getParameterTypes()[0].getTheClass()); // 將調用者強制轉換為DGM方法第一個參數的類型
           7     loadParameters(method,2,mv);
           8     mv.visitMethodInsn(INVOKESTATIC, BytecodeHelper.getClassInternalName(method.getDeclaringClass().getTheClass()), method.getName(), methodDescriptor); // 通過invokestatic指令直接調用DGM方法
           9     BytecodeHelper.box(mv, returnType);
          10     if (method.getReturnType() == void.class) {
          11         mv.visitInsn(ACONST_NULL);
          12     }
          13     mv.visitInsn(ARETURN); // 返回值
          14     mv.visitMaxs(00);
          15     mv.visitEnd();
          16 }
          17 
          18 protected static void loadParameters(CachedMethod method, int argumentIndex, MethodVisitor mv) {
          19     CachedClass[] parameters = method.getParameterTypes();
          20     int size = parameters.length-1;
          21     for (int i = 0; i < size; i++) {
          22         mv.visitVarInsn(ALOAD, argumentIndex);
          23         BytecodeHelper.pushConstant(mv, i);
          24         mv.visitInsn(AALOAD);
          25         Class type = parameters[i+1].getTheClass();
          26         BytecodeHelper.doCast(mv, type); // 將第i個參數強制轉換為DGM方法中第i+1個參數的類型
          27     }
          28 }

          MetaClassRegistryImpl在初始化的時候,會調用自身的registerMethods方法來從META-INF/dgminfo文件中讀取DGM方法和包裝類的對應關系:
           1 private void registerMethods(final Class theClass, final boolean useMethodWrapper, final boolean useInstanceMethods, Map<CachedClass, List<MetaMethod>> map) {
           2     if (useMethodWrapper) { // 我們只看useMethodWrapper為true的情況
           3         try {
           4             List<GeneratedMetaMethod.DgmMethodRecord> records = GeneratedMetaMethod.DgmMethodRecord.loadDgmInfo(); // 從META-INF/dgminfo文件中讀取DGM方法和包裝類的對應關系
           5 
           6             for (GeneratedMetaMethod.DgmMethodRecord record : records) {
           7                 Class[] newParams = new Class[record.parameters.length - 1];
           8                 System.arraycopy(record.parameters, 1, newParams, 0, record.parameters.length-1);
           9 
          10                 MetaMethod method = new GeneratedMetaMethod.Proxy(
          11                         record.className,
          12                         record.methodName,
          13                         ReflectionCache.getCachedClass(record.parameters[0]),
          14                         record.returnType,
          15                         newParams
          16                 ); // 為包裝類創建GeneratedMetaMethod.Proxy代理
          17                 final CachedClass declClass = method.getDeclaringClass();
          18                 List<MetaMethod> arr = map.get(declClass);
          19                 if (arr == null) {
          20                     arr = new ArrayList<MetaMethod>(4);
          21                     map.put(declClass, arr);
          22                 }
          23                 arr.add(method);
          24                 instanceMethods.add(method);
          25             }
          26         } catch (Throwable e) {
          27             
          28     } else {
          29         
          30     }
          31 }

          最后來看看GeneratedMetaMethod.Proxy如何實現延遲加載包裝類:
           1 public static class Proxy extends GeneratedMetaMethod {
           2     private volatile MetaMethod proxy;
           3     private final String className;
           4     
           5     public Object invoke(Object object, Object[] arguments) {
           6         return proxy().invoke(object, arguments);
           7     }
           8 
           9     public final synchronized MetaMethod proxy() {
          10         // 第一次調用時,通過createProxy創建包裝類實例
          11         if (proxy == null) {
          12             createProxy();
          13         }
          14         return proxy;
          15     }
          16 
          17     private void createProxy() {
          18         try {
          19             // 載入包裝類并進行實例化
          20             Class<?> aClass = getClass().getClassLoader().loadClass(className.replace('/','.'));
          21             Constructor<?> constructor = aClass.getConstructor(String.class, CachedClass.class, Class.class, Class[].class);
          22             proxy = (MetaMethod) constructor.newInstance(getName(), getDeclaringClass(), getReturnType(), getNativeParameterTypes());
          23         }
          24         catch (Throwable t) {
          25             t.printStackTrace();
          26             throw new GroovyRuntimeException("Failed to create DGM method proxy : " + t, t);
          27         }
          28     }
          29 }

          以上分析有不當之處敬請指出,謝謝大家的閱讀。

          posted on 2011-04-26 15:24 Johnny Jian 閱讀(2766) 評論(0)  編輯  收藏 所屬分類: Groovy

          主站蜘蛛池模板: 周口市| 尼玛县| 乌什县| 芮城县| 丘北县| 沐川县| 固安县| 东安县| 称多县| 曲水县| 酒泉市| 娄烦县| 如皋市| 绍兴县| 磐安县| 平顺县| 漳平市| 二连浩特市| 淮滨县| 南昌市| 濮阳县| 芮城县| 民乐县| 玉溪市| 东莞市| 潢川县| 伊金霍洛旗| 平利县| 勐海县| 唐山市| 丽江市| 辽宁省| 揭西县| 广西| 修水县| 东兰县| 雷波县| 泽普县| 云林县| 玉田县| 柳林县|