<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 4, 文章 - 0, 評論 - 2, 引用 - 0
    數(shù)據(jù)加載中……

    Groovy深入探索——DGM調(diào)用優(yōu)化

    DGM調(diào)用優(yōu)化是通過直接調(diào)用代替反射調(diào)用的方式來提高DGM方法的調(diào)用效率。

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

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

    反射調(diào)用和直接調(diào)用
    在Groovy中,用于表示一個方法的是一個MetaMethod(這是一個抽象類)實例,MetaMethod類似于JDK中的Method。而在大多數(shù)情況下,這個表示方法的實例會是CachedMethod(MetaMethod的派生類)類型的。調(diào)用其代表的方法時,會調(diào)用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中一般是通過反射的方式調(diào)用方法的。
    眾所周知,在Java中,通過反射來調(diào)用方法,比直接調(diào)用方法會慢上幾倍。因此,DGM調(diào)用優(yōu)化的思想就是通過直接調(diào)用來代替反射調(diào)用。而要實現(xiàn)直接調(diào)用,則需要為DGM中的每個方法生成一個從MetaMethod派生的包裝類,該類的invoke方法將直接調(diào)用DGM中對應(yīng)的方法。

    DGM調(diào)用優(yōu)化
    DGM調(diào)用優(yōu)化的大體流程是這樣的:

    1. 在編譯Groovy自身的Java代碼(不是用Groovy寫的代碼)之后,通過調(diào)用DgmConverter類的main方法,為DefaultGroovyMethods的每個方法生成一個包裝類,該類繼承GeneratedMetaMethod類,而GeneratedMetaMethod類則繼承MetaMethod類。該包裝類的類名類似于org.codehaus.groovy.runtime.dgm$123($后跟一個數(shù)),在Groovy分發(fā)的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]); // 將各參數(shù)強制轉(zhuǎn)換為對應(yīng)的類型后,直接調(diào)用DefaultGroovyMethods中對應(yīng)的方法
    5     }
    6     
    7 }

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

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

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

    那為什么不為用戶寫的Groovy程序的每一個方法,在運行時動態(tài)的創(chuàng)建這樣直接調(diào)用的包裝類,來提高每個方法的調(diào)用效率呢?那是因為這樣會產(chǎn)生大量的類,由于每個類都是需要占用內(nèi)存的,所以會對JVM用于存放類信息的PermGen內(nèi)存區(qū)域造成壓力,容易產(chǎn)生OutOfMemoryError。而DGM方法是Groovy程序中大量被調(diào)用的方法(即熱點),而且數(shù)量只有不到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方法的第一個參數(shù)表示該方法應(yīng)添加到哪個類上,因此DGM方法至少有一個參數(shù)
    26           continue;
    27 
    28         final Class returnType = method.getReturnType();
    29         final String className = "org/codehaus/groovy/runtime/dgm$" + cur;
    30 
    31         // record記錄著DGM方法和包裝類的對應(yīng)關(guān)系
    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         // 創(chuàng)建一個繼承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); // 我們只關(guān)注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方法和包裝類的對應(yīng)關(guān)系寫入META-INF/dgminfo文件
    60 }

    我們再來看看用于創(chuàng)建包裝類的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); // 創(chuàng)建一個public Object invoke(Object object, Object[] arguments)方法
     4     mv.visitCode();
     5     mv.visitVarInsn(ALOAD,1);
     6     BytecodeHelper.doCast(mv, method.getParameterTypes()[0].getTheClass()); // 將調(diào)用者強制轉(zhuǎn)換為DGM方法第一個參數(shù)的類型
     7     loadParameters(method,2,mv);
     8     mv.visitMethodInsn(INVOKESTATIC, BytecodeHelper.getClassInternalName(method.getDeclaringClass().getTheClass()), method.getName(), methodDescriptor); // 通過invokestatic指令直接調(diào)用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個參數(shù)強制轉(zhuǎn)換為DGM方法中第i+1個參數(shù)的類型
    27     }
    28 }

    MetaClassRegistryImpl在初始化的時候,會調(diào)用自身的registerMethods方法來從META-INF/dgminfo文件中讀取DGM方法和包裝類的對應(yīng)關(guān)系:
     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方法和包裝類的對應(yīng)關(guān)系
     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                 ); // 為包裝類創(chuàng)建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如何實現(xiàn)延遲加載包裝類:
     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         // 第一次調(diào)用時,通過createProxy創(chuàng)建包裝類實例
    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 閱讀(2763) 評論(0)  編輯  收藏 所屬分類: Groovy

    主站蜘蛛池模板: 亚洲中久无码永久在线观看同| 免费一级毛片在线播放视频免费观看永久 | 最近中文字幕国语免费完整| 国产在亚洲线视频观看| 亚洲日本在线播放| 亚洲国产精品VA在线观看麻豆| 四虎影在线永久免费观看| 噼里啪啦免费观看高清动漫4| 午夜精品一区二区三区免费视频 | 无码国产亚洲日韩国精品视频一区二区三区| 色老头永久免费网站| 久久青青草原国产精品免费| 一级做α爱过程免费视频| 久久人午夜亚洲精品无码区| 亚洲高清中文字幕免费| 亚洲国产精品成人综合色在线婷婷| 亚洲va久久久噜噜噜久久狠狠| 国产成人综合亚洲亚洲国产第一页| 亚洲高清无码专区视频| 四虎永久在线精品免费观看地址 | 亚洲毛片一级带毛片基地| 亚洲爆乳无码专区| 亚洲乱码中文字幕久久孕妇黑人| 亚洲乱码国产一区网址| 高清在线亚洲精品国产二区| 免费鲁丝片一级观看| 最新中文字幕电影免费观看| 四虎免费大片aⅴ入口| 成人免费毛片观看| 成熟女人特级毛片www免费| 免费人成视频在线| 免费无码又爽又高潮视频| 日韩免费在线观看| 免费激情视频网站| 四虎永久在线精品视频免费观看| 四虎影视永久免费观看| www.91亚洲| 亚洲中久无码永久在线观看同| 国产成人亚洲综合无码精品 | 最近2018中文字幕免费视频 | 亚洲小说区图片区|