日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java反射在JVM的实现

發(fā)布時(shí)間:2025/3/21 java 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java反射在JVM的实现 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文目錄

  • 什么是Java反射,有什么用?
  • Java Class文件的結(jié)構(gòu)
  • Java Class加載的過(guò)程
  • 反射在native的實(shí)現(xiàn)
  • 附錄

  • 1. 什么是Java反射,有什么用?

    反射使程序代碼能夠接入裝載到JVM中的類的內(nèi)部信息,允許在編寫與執(zhí)行時(shí),而不是源代碼中選定的類協(xié)作的代碼,是以開(kāi)發(fā)效率換運(yùn)行效率的一種手段。這使反射成為構(gòu)建靈活應(yīng)用的主要工具。

    反射可以:

  • 調(diào)用一些私有方法,實(shí)現(xiàn)黑科技。比如雙卡短信發(fā)送、設(shè)置狀態(tài)欄顏色、自動(dòng)掛電話等。
  • 實(shí)現(xiàn)序列化與反序列化,比如PO的ORM,Json解析等。
  • 實(shí)現(xiàn)跨平臺(tái)兼容,比如JDK中的SocketImpl的實(shí)現(xiàn)
  • 通過(guò)xml或注解,實(shí)現(xiàn)依賴注入(DI),注解處理,動(dòng)態(tài)代理,單元測(cè)試等功能。比如Retrofit、Spring或者Dagger
  • 2. Java Class文件的結(jié)構(gòu)

    在*.class文件中,以Byte流的形式進(jìn)行Class的存儲(chǔ),通過(guò)一系列Load,Parse后,Java代碼實(shí)際上可以映射為下圖的結(jié)構(gòu)體,這里可以用javap命令或者IDE插件進(jìn)行查看。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 typedef struct { ????u4???????????? magic;/*0xCAFEBABE*/ ????u2???????????? minor_version; /*網(wǎng)上有表可查*/ ????u2???????????? major_version; /*網(wǎng)上有表可查*/ ????u2???????????? constant_pool_count; ????cp_info??????? constant_pool[constant_pool_count-1]; ????u2???????????? access_flags; ????u2???????????? this_class; ????u2???????????? super_class; ????u2???????????? interfaces_count; ????u2???????????? interfaces[interfaces_count]; ????//重要 ????u2???????????? fields_count; ????field_info???? fields[fields_count]; ????//重要 ????u2???????????? methods_count; ????method_info??? methods[methods_count]; ????u2???????????? attributes_count; ????attribute_info attributes[attributes_count]; }ClassBlock;
    • 常量池(constant pool):類似于C中的DATA段與BSS段,提供常量、字符串、方法名等值或者符號(hào)(可以看作偏移定值的指針)的存放
    • access_flags: 對(duì)Class的flag修飾
      1 2 3 4 5 6 7 typedef enum { ??????ACC_PUBLIC = 0x0001, ??????ACC_FINAL = 0x0010, ??????ACC_SUPER = 0x0020, ??????ACC_INTERFACE = 0x0200, ??????ACC_ACSTRACT = 0x0400 ??}AccessFlag
    • this class/super class/interface: 一個(gè)長(zhǎng)度為u2的指針,指向常量池中真正的地址,將在Link階段進(jìn)行符號(hào)解引。
    • filed: 字段信息,結(jié)構(gòu)體如下
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 typedef struct fieldblock { ?????char *name; ?????char *type; ?????char *signature; ?????u2 access_flags; ?????u2 constant; ?????union { ?????????union { ?????????????char data[8]; ?????????????uintptr_t u; ?????????????long long l; ?????????????void *p; ?????????????int i; ?????????} static_value; ?????????u4 offset; ?????} u; ??} FieldBlock;
    • method: 提供descriptor, access_flags, Code等索引,并指向常量池:

    它的結(jié)構(gòu)體如下,詳細(xì)在這里

    1 2 3 4 5 6 7 8 9 method_info { ?????u2???????????? access_flags; ?????u2???????????? name_index; ?????//the parameters that the method takes and the ?????//value that it return ?????u2???????????? descriptor_index; ?????u2???????????? attributes_count; ?????attribute_info attributes[attributes_count]; ?}

    以上具體內(nèi)容可以參考

  • JVM文檔
  • 周志明的《深入理解Java虛擬機(jī)》,少見(jiàn)的國(guó)內(nèi)精品書(shū)籍
  • 一些國(guó)外教程的解析
  • 3. Java Class加載的過(guò)程

    Class的加載主要分為兩步

    • 第一步通過(guò)ClassLoader進(jìn)行讀取、連結(jié)操作
    • 第二步進(jìn)行Class的<clinit>()初始化。

    3.1. Classloader加載過(guò)程

    ClassLoader用于加載、連接、緩存Class,可以通過(guò)純Java或者native進(jìn)行實(shí)現(xiàn)。在JVM的native代碼中,ClassLoader內(nèi)部維護(hù)著一個(gè)線程安全的HashTable<String,Class>,用于實(shí)現(xiàn)對(duì)Class字節(jié)流解碼后的緩存,如果HashTable中已經(jīng)有了緩存,則直接返回緩存;反之,在獲得類名后,通過(guò)讀取文件、網(wǎng)絡(luò)上的class字節(jié)流反序列化為JVM中native的C結(jié)構(gòu)體,接著malloc內(nèi)存,并將指針緩存在HashTable中。

    下面是非數(shù)組情況下ClassLoader的流程

    • find/load: 將文件反序列化為C結(jié)構(gòu)體。

    Class反序列化的流程
    • link: 根據(jù)Class結(jié)構(gòu)體常量池進(jìn)行符號(hào)的解引。比如對(duì)象計(jì)算內(nèi)存空間,創(chuàng)建方法表,native invoker,接口方法表,finalizer函數(shù)等工作。

    3.2. 初始化過(guò)程

    當(dāng)ClassLoader加載Class結(jié)束后,將進(jìn)行Class的初始化操作。主要執(zhí)行<clinit()>的靜態(tài)代碼段與靜態(tài)變量(取決于源碼順序)。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Sample { ??//step.1 ??static int b = 2; ??//step.2 ??static { ????b = 3; ??} ??public static void main(String[] args) { ????Sample s = new Sample(); ????System.out.println(s.b); ????//b=3 ??} }

    具體參考如下:

    • When and how a Java class is loaded and initialized?
    • The Lifetime of a Type

    在完成初始化后,就是Object的構(gòu)造<init>了,本文暫不討論。

    4. 反射在native的實(shí)現(xiàn)

    反射在Java中可以直接調(diào)用,不過(guò)最終調(diào)用的仍是native方法,以下為主流反射操作的實(shí)現(xiàn)。

    4.1. Class.forName的實(shí)現(xiàn)

    Class.forName可以通過(guò)包名尋找Class對(duì)象,比如Class.forName("java.lang.String")。
    在JDK的源碼實(shí)現(xiàn)中,可以發(fā)現(xiàn)最終調(diào)用的是native方法forName0(),它在JVM中調(diào)用的實(shí)際是findClassFromClassLoader(),原理與ClassLoader的流程一樣,具體實(shí)現(xiàn)已經(jīng)在上面介紹過(guò)了。

    4.2. getDeclaredFields的實(shí)現(xiàn)

    在JDK源碼中,可以知道class.getDeclaredFields()方法實(shí)際調(diào)用的是native方法getDeclaredFields0(),它在JVM主要實(shí)現(xiàn)步驟如下

  • 根據(jù)Class結(jié)構(gòu)體信息,獲取field_count與fields[]字段,這個(gè)字段早已在load過(guò)程中被放入了
  • 根據(jù)field_count的大小分配內(nèi)存、創(chuàng)建數(shù)組
  • 將數(shù)組進(jìn)行forEach循環(huán),通過(guò)fields[]中的信息依次創(chuàng)建Object對(duì)象
  • 返回?cái)?shù)組指針
  • 主要慢在如下方面

  • 創(chuàng)建、計(jì)算、分配數(shù)組對(duì)象
  • 對(duì)字段進(jìn)行循環(huán)賦值
  • 4.3. Method.invoke的實(shí)現(xiàn)

    以下為無(wú)同步、無(wú)異常的情況下調(diào)用的步驟

  • 創(chuàng)建Frame
  • 如果對(duì)象flag為native,交給native_handler進(jìn)行處理
  • 在frame中執(zhí)行java代碼
  • 彈出Frame
  • 返回執(zhí)行結(jié)果的指針
  • 主要慢在如下方面

  • 需要完全執(zhí)行ByteCode而缺少JIT等優(yōu)化
  • 檢查參數(shù)非常多,這些本來(lái)可以在編譯器或者加載時(shí)完成
  • 4.4. class.newInstance的實(shí)現(xiàn)

  • 檢測(cè)權(quán)限、預(yù)分配空間大小等參數(shù)
  • 創(chuàng)建Object對(duì)象,并分配空間
  • 通過(guò)Method.invoke調(diào)用構(gòu)造函數(shù)(<init>())
  • 返回Object指針
  • 主要慢在如下方面

  • 參數(shù)檢查不能優(yōu)化或者遺漏
  • <init>()的查表
  • Method.invoke本身耗時(shí)
  • 5. 附錄

    5.1. JVM與源碼閱讀工具的選擇

    初次學(xué)習(xí)JVM時(shí),不建議去看Android Art、Hotspot等重量級(jí)JVM的實(shí)現(xiàn),它內(nèi)部的防御代碼很多,還有android與libcore、bionic庫(kù)緊密耦合,以及分層、內(nèi)聯(lián)甚至能把編譯器的語(yǔ)義分析繞進(jìn)去,因此找一個(gè)教學(xué)用的、嵌入式小型的JVM有利于節(jié)約自己的時(shí)間。因?yàn)橐郧罢垓v過(guò)OpenWrt,聽(tīng)過(guò)有大神推薦過(guò)jamvm,只有不到200個(gè)源文件,非常適合學(xué)習(xí)。

    在工具的選擇上,個(gè)人推薦SourceInsight。對(duì)比了好幾個(gè)工具clion,vscode,sublime,sourceinsight,只有sourceinsight對(duì)索引、符號(hào)表的解析最準(zhǔn)確。

    5.2. 關(guān)于幾個(gè)ClassLoader

    參考這里

    ClassLoader0:native的classloader,在JVM中用C寫的,用于加載rt.jar的包,在Java中為空引用。

    ExtClassLoader: 用于加載JDK中額外的包,一般不怎么用

    AppClassLoader: 加載自己寫的或者引用的第三方包,這個(gè)最常見(jiàn)

    例子如下

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //sun.misc.Launcher$AppClassLoader@4b67cf4d //which class you create or jars from thirdParty //第一個(gè)非常有歧義,但是它的確是AppClassLoader ClassLoader.getSystemClassLoader(); com.test.App.getClass().getClassLoader(); Class.forName("ccom.test.App").getClassLoader() //sun.misc.Launcher$ExtClassLoader@66d3c617 //Class loaded in ext jar Class.forName("sun.net.spi.nameservice.dns.DNSNameService") //null, class loaded in rt.jar String.class.getClassLoader() Class.forName("java.lang.String").getClassLoader() Class.forName("java.lang.Class").getClassLoader() Class.forName("apple.launcher.JavaAppLauncher").getClassLoader()

    最后就是getContextClassLoader(),它在Tomcat中使用,通過(guò)設(shè)置一個(gè)臨時(shí)變量,可以向子類ClassLoader去加載,而不是委托給ParentClassLoader

    1 2 3 4 5 6 7 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); try { ????Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); ????// call some API that uses reflection without taking ClassLoader param } finally { ????Thread.currentThread().setContextClassLoader(originalClassLoader); }

    最后還有一些自定義的ClassLoader,實(shí)現(xiàn)加密、壓縮、熱部署等功能,這個(gè)是大坑,晚點(diǎn)再開(kāi)。

    5.3. 反射是否慢?

    在Stackoverflow上認(rèn)為反射比較慢的程序員主要有如下看法

  • 驗(yàn)證等防御代碼過(guò)于繁瑣,這一步本來(lái)在link階段,現(xiàn)在卻在計(jì)算時(shí)進(jìn)行驗(yàn)證
  • 產(chǎn)生很多臨時(shí)對(duì)象,造成GC與計(jì)算時(shí)間消耗
  • 由于缺少上下文,丟失了很多運(yùn)行時(shí)的優(yōu)化,比如JIT(它可以看作JVM的重要評(píng)測(cè)標(biāo)準(zhǔn)之一)
  • 當(dāng)然,現(xiàn)代JVM也不是非常慢了,它能夠?qū)Ψ瓷浯a進(jìn)行緩存以及通過(guò)方法計(jì)數(shù)器同樣實(shí)現(xiàn)JIT優(yōu)化,所以反射不一定慢。

    更重要的是,很多情況下,你自己的代碼才是限制程序的瓶頸。因此,在開(kāi)發(fā)效率遠(yuǎn)大于運(yùn)行效率的的基礎(chǔ)上,大膽使用反射,放心開(kāi)發(fā)吧。

    參考文獻(xiàn)

  • http://www.codeceo.com/article/reflect-bad.html
  • http://blog.csdn.net/lmj623565791/article/details/43452969
  • http://codekk.com/open-source-project-analysis/detail/Android/Trinea/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8BJava%20%E6%B3%A8%E8%A7%A3%20Annotation
  • http://www.trinea.cn/android/java-annotation-android-open-source-analysis/
  • 原文出處:?BlackSwift(簡(jiǎn)書(shū)作者)
    from:?http://www.importnew.com/21211.html

    總結(jié)

    以上是生活随笔為你收集整理的Java反射在JVM的实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。