999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

提高Java程序動態性的一個新途徑

2015-10-21 18:12:24嚴忠林
計算機時代 2015年9期

嚴忠林

摘 要: 為支持Groovy、JRuby等新的動態類型語言,JDK1.7在Java虛擬機上特意引入了新的動態調用指令。文章提出將其應用于Java程序,在生成的Java類文件中用它替換某些成員訪問指令,由此可以突破Java原本固有的運行模式,引入滿足應用需要的新運行機制,使程序更簡單、靈活,提高開發效率。同時分析了原有成員訪問指令的局限,討論了新的動態調用指令的優勢,給出了指令替換的實現方法。

關鍵詞: 動態調用指令; 成員訪問; Java虛擬機; Java類文件

中圖分類號:TP311 文獻標志碼:A 文章編號:1006-8228(2015)09-01-03

New approach to improve dynamic of Java programs

Yan Zhonglin

(College of information, mechanical and electrical engineering, Shanghai Normal University, Shanghai 200234, China)

Abstract: Since JDK1.7, the invokedynamic instruction has been introduced to JVM to support dynamically typed languages such as Groovy, JRuby. This paper proposes replacing some appropriate member access instructions with it in a Java class file, which may break Java inherent operation mode, introduce a new mechanism that meets the application needs, make the program more simple and flexible, and improve development efficiency. At the same time, the limitations of the original member access instruction and the advantages of the invokedynamic instruction is analyzed, and the method to realize the instruction replacement is given.

Key words: invokedynamic instruction; member access; JVM; Java class file

0 引言

自JDK1.7起,為了方便在JVM上實現像JRuby、Jython、Groovy、Clojure那樣的動態類型語言,在虛擬機層面引入了動態類型語言支持(JSR-292)[1]。與Java這樣的靜態類型語言不同,動態類型語言的變量、方法不需要事先聲明,直到運行時,才根據當時數據的實際類型決定所要進行的操作。例如對表達式a+b,要一直到運行至此處時,才根據a、b當時的數值,決定是做整數加、浮點數加,還是字符串加。因此對于這類語言,計算機所執行的代碼不是在編譯時,而要到運行時才確定。

所謂動態性是指這種能根據運行時狀態,自主決定實際操作的能力。Java本是靜態類型語言,大多數操作都不能在運行時改變,與此相適應的JVM指令也因此很難滿足動態類型語言的要求。為改變這種狀況,JSR-292引入了新的動態調用指令invokedynamic,它執行的操作取決于內含的方法句柄(MethodHandle)。MethodHandle是新添加的java.lang.invoke包中的類,是對類內各種成員的引用[2]。使用它,動態類型語言就能按照自己的規則,根據運行時參數和其他狀態,實現希望的操作。

新指令原本是為動態語言而設計的,但我們嘗試將它用于Java程序,替換某些成員訪問指令。這樣可以改變Java一些固有的行為模式,引入希望的運行機制,給軟件開發帶來便利。這種替換不要求改變運行時方法棧中的參數和返回值,能保持Java原有的語法習慣和可理解性。

1 成員訪問指令的特性

JVM原有的數據訪問指令有getfield/putfield(獲得/修改實例變量),getstatic/putstatic(獲得/修改類變量)。方法調用有invokestatic(調用類方法),invokespecial(調用可在編譯時綁定的實例方法,如構造、private、super方法等),invokevirtual(調用動態綁定的實例方法),invokeinterface(調用接口定義的方法)。Java程序中所有成員訪問都是通過這8條指令實現的[3]。它們是專為Java設計的,有固定的處理流程,有良好的執行效率,多數情況下能滿足要求。但由于缺乏靈活性,在某些場合,也會出現不便使用,導致程序復雜化的情形。

這些指令除最后兩條外,所做的操作在運行前就已確定,不能在運行時動態改變。以圖1的處理點(Point)、線(Line)的代碼為例,Line對象用k、b記錄直線的斜率和y軸截距,但對垂直于x軸的直線,這倆數據都無意義,還需構建子類VLine,添加新的b字段,記錄x軸的截距(此處僅為說明問題,并非倡導此種設計。繼承關系也極簡單,實際上只有在相當規模和復雜度的系統中,此處討論的缺乏動態性帶來的不便才會突顯出來)。語句①本來是想獲得數組中各直線的有意義的截距進行處理,但實際上卻只能獲得y軸的截距。傳統上解決此類問題的途徑是在相關類中編寫大量get/set方法,這將使程序臃腫龐大,把一個簡單操作復雜化了。

方法調用也有類似問題,用super可調用父類方法,但和this不同,它是靜態的。圖1中的語句②會根據this對象的不同執行不同的f0,但語法上極其類似的語句③永遠只會執行GElem類中的f0,如果設計者的真實想法是調用每個對象自己的父類中的方法,恐怕又得添加很多輔助代碼[4]。

即使是采用動態綁定的最后兩條指令(invokevirtual和invokeinterface),其動態性也是有限的。它只能根據方法的第一個參數(即this引用)的不同作出選擇。當處理方式不取決于單個對象,而必須考慮所有參與處理的對象時,就會帶來困難。例如,為求兩直線交點,在Line和VLine中分別重載實現了針對不同類型直線的crossPoint方法。main()中語句④想求出數組內任意兩條直線間的交點,但它是錯誤的。要完成此任務,必須先用if語句作類型判斷,再選擇合適的方法體。這顯然增加了程序復雜性,更重要的是這種“硬編碼”會破壞系統的可擴展性,如果再要添加一個子類,將不得不修改所有相關的if語句,對于大系統,這絕不是一件輕松的工作。

2 動態調用指令的優勢

上述問題都是由于對應的成員訪問指令只能按照固定模式處理,缺乏動態性造成的。如使用新的invokedynamic指令替代,就能引入新的執行模式,讓其在運行時根據實際參數動態選擇合適的數據、方法,這些問題就可在bytecode層面解決了,上層的Java源代碼則不必做任何改變。由于新指令中的方法句柄是自定義的,因此可根據需要實現多種特定機制,運行時不但可做類型判斷,還可進行各種添加、變換。比如變量賦值前進行合法性校驗,調用關鍵操作時作日志記錄,將對某個方法的調用轉為對其他方法的調用等等,面向方面編程(AOP)所需的在方法調用前后“編織”的橫切操作,都可以在這兒實現。

這種通過使用invokedynamic指令引入新機制、獲得動態性的方法,實際上把系統實現分成了上、下兩層,上層是和具體事務相關的業務邏輯,下層是根據參數和其他狀態進行選擇、變換、處理的機制實現。只要設計合理,兩者可以清晰劃分。動態機制在上層看來,是自動實現的,可直接使用,編程時只需專注于業務處理。而下層實現,比如根據參數類型選擇重載方法等,也和上層絕少關聯。這對軟件的開發、維護無疑都是極其有利的。

在過去,要在程序中實現各種動態機制,大多要用到“反射”。它有一套特殊的API,通常難以將處理過程隱藏于無形,會增加程序的復雜性。更重要的是它還會影響程序的執行效率,眾所周知,現代Java程序的高效運行是通過JIT、Hotspot等技術將bytecode轉為本地碼,并采用大量積極的優化措施取得的。而“反射”機制不在bytecode層面實現,無法使用這些手段,因而其運行是低效的。

與“反射”相比,invokedynamic指令更有優勢。動態實現都隱藏于最基本的訪問指令中,上層代碼就是普通的Java程序,直接表達事務的處理過程,清晰自然。它還便于系統的修改、切換,例如在試驗階段,底層可添加檢查、校驗等功能,到成品階段能方便地撤除。在A環境下的一些操作,到B環境下可用另一些方法替換。這些修改只發生在下層,上層源代碼不需改變。

從運行效率上來看,它比“反射”也更有利。JSR-292的設計目標就是要讓動態類型語言能在JVM上高速運行,所有MethodHandle都直接在虛擬機層面執行。為保證高效率,甚至于在其他訪問指令中每次都要執行的權限檢查操作,也被挪到該對象初始構造時進行,執行時沒有性能消耗。JIT、Hotspot等技術帶來的豐富的優化措施,也可充分利用。所以使用它可以獲得很高的執行效率。

3 指令替換的實現

JSR-292是為新型語言而設計,沒有打算用于Java。所以通過Java編譯器不可能生成invokedynamic指令,我們只能在編譯獲得的類文件中自行完成需要的替換。好在Java程序各個類分開存儲,有定義明確的格式和語義(圖2),較易于理解和處理。

類文件的常量池含有代碼中使用的所有常量和標識符,以及各種類型表達。類本身、各數據、方法的細節信息都通過對常量池的索引獲得描述。一個類和其他類的關聯、對其他類的訪問也基于常量池中的符號引用進行。bytecode代碼出現于對應方法的Code屬性中,在我們關注的成員訪問指令中有編譯時確定的常量池索引,指出了它所訪問的數據或方法。

要進行替換,首先要找到這些指令,為此定義了標注DynReplace(圖3)。其中Instruction是成員訪問指令的枚舉,用于說明查找指令的種類,refClass、refName、refType是對被訪問成員所在類、名字和類型的描述,構成對它的符號引用。bsmClass、bsmMethod指出替換后新指令所需代碼所在的類和方法名。這是一個可重復標注,可同時說明需要替換的多條指令。它們用于啟動類前,處理程序將從啟動類開始,遍歷相關類文件,進行搜尋處理。

[public @interface DynReplace { Instruction instr();

String refClass(); String refName(); String refType();

String bsmClass(); String bsmMethod();

}]

圖3 DynReplace標注

每條invokedynamic指令都對應一個CallSite對象,它含有指令執行所需的方法句柄。但指令首次執行時,該對象不存在,需執行一個特殊的自舉方法(Bootstrap Method),生成此對象。JSR-292為此在類文件中引入新結構,在類屬性池中加入了BootstrapMethods屬性,這是一個自舉方法句柄數組,通過常量池索引指出各條invokedynamic指令的自舉方法。常量池中也引入三種新類型常量,MethodHandle_info(句柄的類型和對應方法的符號引用),MethodType_info(句柄的參數、返回值描述),InvokeDynamic_info(被invokedynamic指令直接使用,指出它的自舉方法句柄索引和調用的方法名及類型參數)。

每條替換用的invokedynamic指令都需要以下代碼,這些代碼應出現在上述標注的bsmClass字段指定的類中。作為舉例,圖4是對圖1中語句④的調用指令進行替換,使它能正確執行所需提供的類代碼。

[public class Handle { static HashMap map=

new HashMap();

private static void putMHs() {

Class<?> c0= Point.class, c1= Line.class, c2= VLine.class, ... ...

map.put(methodType(c1, c2), //將各方法句柄按對應參數放入map中

lookup().findVirtual(c1, "crossPoint", methodType(c0, c2)));

public static Point crossPoint(Line la, Line lb) {

MethodHandle mh=map.get(methodType(la.getClass(),lb.getClass()));

return (Point) mh.invoke(la,lb);

public static CallSite bsm(Lookup caller, String name, MethodType type) {

putMHs();

MethodHandle mh=caller.findStatic(Handle.class, name, type);

return new ConstantCallSite(mh);

圖4 示例代碼2

⑴ 代替原來的成員訪問而實現希望的動態機制的方法。它可以參照參數和其他狀態選擇執行代碼,也可插入“橫切”操作。它為static方法,參數應與原指令在方法棧中的參數相同。圖4中的crossPoint()即為此方法,它用兩個參數的實際類型構造MethodType對象,以此為key在散列表map中獲取方法句柄加以執行。使用散列表的原因是為了避免在參數個數較多,繼承關系復雜的情況下做過多的if判斷,以保證執行效率。

⑵ 可選的初始化方法。如果動態處理方法需要初始化操作,應提供該方法,它將在invokedynamic指令首次執行時,在其自舉方法中被調用。圖4中的putMHs()即為該方法,它生成需要的map對象,為crossPoint()調用時參數的各種可能組合準備好正確的方法句柄。這里以MethodType為key,可防止生成過多的對象。MethodType是為描述方法句柄返回值和參數引入的不可變對象,它的methodType方法對相同的類型只生成一個對象,以后可一直復用。這樣就避免了每次調用crossPoint()時產生新對象,保證了運行效率。

⑶ invokedynamic指令首次執行需要的自舉方法,該方法有固定參數,僅執行一次。它應該用上述的動態處理方法句柄構造CallSite對象并返回,如有初始化方法也應先執行之。圖4中的bsm即是該方法。其方法名應出現在標注的bsmMethod字段,這樣執行指令替換的代碼能將其置入新指令中。

執行指令替換的代碼要對類文件做下列操作。實現這些操作需要熟悉Java類文件的結構。筆者在學習過程中制作了一個解析和處理程序,但讀者不必一切從頭開始,完全可以使用已有的ASM[5]等開源工具來完成。

⑷ 生成正確的invokedynamic指令。該指令內有指向常量池中InvokeDynamic_info的索引,通過它可在BootstrapMethods屬性中獲得對應的自舉方法句柄,該句柄也在常量池中用MethodHandle_info描述。因此在常量池和類屬性池中需要正確添加這些元素。

⑸ 進行指令替換。在方法代碼中將原成員訪問指令替換為對應的invokedynamic指令。由于該指令長度是5byte,而舊指令長度有3和5兩種,故替換時,可能要將后續代碼下移。這又會引起代碼長度、轉移偏移量以及諸如局部變量、StackMap等聲明的有效范圍的改變,替換時對這些元素也要同時修正。

這五個步驟只有前兩步驟是應該公開的,其他都是與處理業務無關的執行指令替換的細節,完全可以封裝起來,一般程序員不必了解,不會影響他們的開發工作。

4 結束語

用invokedynamic指令替代JVM中原來的各成員訪問指令,其內部的處理規則就不再像舊指令那樣固定不變,可靈活制定。實現的操作能在運行時由計算機根據實際情況自行選擇、變換,更具動態性。能高效地實現原來采用“反射”才能完成的功能。雖然它的實現需要對Java虛擬機有較深的理解,但這僅是對個別核心開發人員的要求,一般程序員只會感覺編程更方便。和原來的成員訪問指令相比,在性能上它也可能有一點損失,但在軟件規模及復雜性日漸增長的今天,提高開發人員的工作效率無疑是最重要的,這里介紹的做法正好符合這種要求。

參考文獻:

[1] John Rose. JSR-292 Supporting Dynamically Typed Languages

on the Java Platform (FinalRelease)[EB/OL].https://jcp.org/aboutJava/communityprocess/final/jsr292/

[2] Oracle co. Java Platform, Standard Edition 8 API Specification

[EB/OL].http:// docs.oracle.com/javase/8/docs/api/

[3] Tim Lindholm, Frank Yellin. The Java Virtual Machine Specifica-

tion Java SE 8 Edition[M] New Jersey Addison-Wesley Professional,2015.2.

[4] 周志明.深入理解Java虛擬機:JVM高級特性與最佳實踐(第2版)[M].

機械工業出版社,2013.

[5] Eric Bruneton. ASM 4.0 A Java bytecode engineering library [EB/OL].

http://download.forge.objectweb.org/asm/asm4-guide.pdf

主站蜘蛛池模板: 国产一区二区三区在线无码| 欧美精品aⅴ在线视频| 国产91特黄特色A级毛片| 成人无码一区二区三区视频在线观看| 国产欧美视频一区二区三区| 欧美色图久久| 久久国产毛片| 亚洲 欧美 中文 AⅤ在线视频| 国产成人91精品免费网址在线| 日韩在线播放中文字幕| 色综合久久久久8天国| 亚洲天堂777| 国产精品自在线拍国产电影| 男人天堂亚洲天堂| 丁香婷婷激情综合激情| 国产激情第一页| 日韩成人午夜| 九九九九热精品视频| 99热这里都是国产精品| 白丝美女办公室高潮喷水视频 | 中文字幕亚洲专区第19页| 亚洲电影天堂在线国语对白| 91小视频在线观看| 五月天婷婷网亚洲综合在线| 日本不卡在线| 四虎在线观看视频高清无码| 最新国语自产精品视频在| 91精品免费高清在线| 国产一二三区在线| 日韩国产亚洲一区二区在线观看| 国产九九精品视频| 日韩精品亚洲一区中文字幕| 天堂网亚洲系列亚洲系列| 欧美日韩免费| h视频在线播放| 国产成人高清精品免费| 美女黄网十八禁免费看| 日韩欧美国产成人| 日韩在线永久免费播放| 欧美在线中文字幕| 欧美日韩激情在线| 国产视频一二三区| 四虎国产成人免费观看| 毛片网站观看| 国产迷奸在线看| 亚洲国产成人麻豆精品| 伊在人亞洲香蕉精品區| 国产美女一级毛片| 91精品伊人久久大香线蕉| 国产日韩精品欧美一区灰| 狠狠亚洲婷婷综合色香| 国产精品天干天干在线观看| 久久久久人妻精品一区三寸蜜桃| 91po国产在线精品免费观看| 中文字幕丝袜一区二区| 国产精品片在线观看手机版| 色香蕉影院| m男亚洲一区中文字幕| 这里只有精品在线播放| 亚洲精品国产精品乱码不卞| 18禁黄无遮挡网站| 久久无码免费束人妻| 久久亚洲中文字幕精品一区| a在线亚洲男人的天堂试看| 国产亚洲欧美在线视频| 国产尤物在线播放| 色有码无码视频| 国产尤物在线播放| 日本午夜视频在线观看| 国产本道久久一区二区三区| 亚洲色偷偷偷鲁综合| 亚洲国产黄色| 秋霞午夜国产精品成人片| 亚洲中文精品人人永久免费| 美女高潮全身流白浆福利区| 99re在线视频观看| 日本久久久久久免费网络| 1级黄色毛片| 国产一区二区色淫影院| 国产精品无码翘臀在线看纯欲| 四虎成人在线视频| 国产在线拍偷自揄观看视频网站|