<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java字節(jié)碼介紹

          共 9849字,需瀏覽 20分鐘

           ·

          2023-05-05 21:41

              



          Java字節(jié)碼 概述

          學(xué)習(xí) Java 的都知道,我們所編寫的?.java?代碼文件通過編譯將會(huì)生成?.class?文件,最初的方式就是通過 JDK 的?javac?指令來編譯,再通過?java?命令執(zhí)行 main 方法所在的類,從而執(zhí)行我們的 Java 程序。而在這中間所生成的 .class 文件中的內(nèi)容,就是 JVM 可以處理運(yùn)行的字節(jié)碼(Byte Code),它由 JVM 解釋為對應(yīng)系統(tǒng)可運(yùn)行的機(jī)器指令,這也是我們的 Java 程序能夠做到一處編譯處處執(zhí)行的原理。

          什么是字節(jié)碼

          Java之所以可以“一次編譯,到處運(yùn)行”。

          • ??一是因?yàn)镴VM針對各種操作系統(tǒng)、平臺(tái)都進(jìn)行了定制。

          • ??二是因?yàn)闊o論在什么平臺(tái),都可以編譯生成固定格式的字節(jié)碼(.class文件)供JVM使用。

          因此,也可以看出字節(jié)碼對于Java生態(tài)的重要性。之所以被稱之為字節(jié)碼,是因?yàn)樽止?jié)碼文件由十六進(jìn)制值組成,而JVM以兩個(gè)十六進(jìn)制值為一組,即以字節(jié)為單位進(jìn)行讀取。在Java中一般是用javac命令編譯源代碼為字節(jié)碼文件,一個(gè).java文件從編譯到運(yùn)行的示例如下:

          36ffe1cccd0c4effb6d6f18c2619f59c.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          對于 Java 開發(fā)人員來說,平時(shí)需要閱讀 Byte Code 的場景比較少,但和閱讀框架源碼能夠了解到框架的設(shè)計(jì)思路一樣,閱讀 Java Byte Code 也有利于我們理解 Java 一些深層的東西,提高我們解決問題的能力。能夠閱讀 Byte Code 也有利于我們?nèi)ダ斫?Kotlin 或其它運(yùn)行在 JVM 上的語言,是如何擴(kuò)展 Java 所沒有的特性或語法。

          字節(jié)碼文件結(jié)構(gòu)

          首先我們先編寫一個(gè)簡單的 Java 代碼作為演示例子,然后編譯這個(gè)?Hello.java?文件得到?Hello.class?文件。我們知道 .class 是二進(jìn)制文件,它無法被直接查看,當(dāng)然我們可以通過一些二進(jìn)制文件查看工具來閱讀里面的內(nèi)容。

          bbb17e1fcf63c34776ddf1b1eabb0cef.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          編譯后生成.class文件,打開后是一堆十六進(jìn)制數(shù),按字節(jié)為單位進(jìn)行分割后展示如上圖右側(cè)部分所示。上文提及過,JVM對于字節(jié)碼是有規(guī)范要求的,那么看似雜亂的十六進(jìn)制符合什么結(jié)構(gòu)呢?JVM規(guī)范要求每一個(gè)字節(jié)碼文件都要由十部分按照固定的順序組成,整體結(jié)構(gòu)如下圖:

          7d45175ac6e103a3f35d0588f6c2bffb.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          魔數(shù)

          一個(gè)符合標(biāo)準(zhǔn)的?.class?文件是以?CA FE BA BE?開頭,這個(gè)四個(gè)字節(jié)均為魔數(shù),JVM 根據(jù)這個(gè)開頭來判斷一個(gè)文件是否可能為?.class?文件,如果是才會(huì)繼續(xù)執(zhí)行。

          有趣的是,魔數(shù)的固定值是Java之父James Gosling制定的,為CafeBabe(咖啡寶貝),而Java的圖標(biāo)為一杯咖啡。

          版本號

          魔數(shù)后面四個(gè)字節(jié)?00 00 00 34?是版本號,前兩個(gè)字節(jié)為次版本號,后兩個(gè)字節(jié)為主版本號,在對主版本號進(jìn)行轉(zhuǎn)換可以得到 52,該序號對應(yīng)的Java 版本為1.8。

          常量池(Constant Pool)

          在版本號后面則是常量池(Constant Pool),它包含常量池計(jì)數(shù)器和常量池?cái)?shù)據(jù)區(qū)兩個(gè)部分。

          55c27a51e2e18d151a2b95310ea866d6.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)
          • ??常量池計(jì)數(shù)器(constant_pool_count):由于常量的數(shù)量不固定,所以需要先放置兩個(gè)字節(jié)來表示常量池容量計(jì)數(shù)值。圖2中示例代碼的字節(jié)碼前10個(gè)字節(jié)如下圖所示,將十六進(jìn)制的24轉(zhuǎn)化為十進(jìn)制值為36,排除掉下標(biāo)“0”,也就是說,這個(gè)類文件中共有35個(gè)常量。

          974c05554e1e52f931236350ba3fd1a5.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)
          • ??常量池?cái)?shù)據(jù)區(qū):數(shù)據(jù)區(qū)是由(constant_pool_count-1)個(gè)cp_info結(jié)構(gòu)組成,一個(gè)cp_info結(jié)構(gòu)對應(yīng)一個(gè)常量。在字節(jié)碼中共有14種類型的cp_info,每種類型的結(jié)構(gòu)都是固定的。

            6fcfcff23e75b67fa092e88c6ae99b9a.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          訪問標(biāo)志

          常量池結(jié)束之后的兩個(gè)字節(jié),描述該Class是類還是接口,以及是否被public、abstract、final等修飾符修飾。JVM規(guī)范規(guī)定了如下圖的訪問標(biāo)志(Access_Flag)。

          需要注意的是,JVM并沒有窮舉所有的訪問標(biāo)志,而是使用按位或操作來進(jìn)行描述的,比如某個(gè)類的修飾符為public final,則對應(yīng)的訪問修飾符的值為ACC_PUBLIC | ACC_FINAL,即0x0001 | 0x0010=0x0011。

          4854194346aa3c604dc8f1e51b0ad83c.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          當(dāng)前類名

          訪問標(biāo)志后的兩個(gè)字節(jié),描述的是當(dāng)前類的全限定名。這兩個(gè)字節(jié)保存的值為常量池中的索引值,根據(jù)索引值就能在常量池中找到這個(gè)類的全限定名。

          父類名稱

          當(dāng)前類名后的兩個(gè)字節(jié),描述父類的全限定名,同上,保存的也是常量池中的索引值。

          接口信息

          父類名稱后為兩字節(jié)的接口計(jì)數(shù)器,描述了該類或父類實(shí)現(xiàn)的接口數(shù)量。緊接著的n個(gè)字節(jié)是所有接口名稱的字符串常量的索引值。

          字段表

          字段表用于描述類和接口中聲明的變量,包含類級別的變量以及實(shí)例變量,但是不包含方法內(nèi)部聲明的局部變量。

          字段表也分為兩部分,

          • ??第一部分為兩個(gè)字節(jié),描述字段個(gè)數(shù);

          • ??第二部分是每個(gè)字段的詳細(xì)信息fields_info。

          字段表結(jié)構(gòu)如下圖

          91f7064e1ac9d2034ebd9c7a546970b5.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          方法表

          字段表結(jié)束后為方法表,方法表也是由兩部分組成,第一部分為兩個(gè)字節(jié)描述方法的個(gè)數(shù);第二部分為每個(gè)方法的詳細(xì)信息。方法的詳細(xì)信息較為復(fù)雜,包括方法的訪問標(biāo)志、方法名、方法的描述符以及方法的屬性,如下圖所示:

          7042efe11aff17076da4a27bdd431021.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          方法的權(quán)限修飾符依然可以通過訪問標(biāo)志查詢得到,方法名和方法的描述符都是常量池中的索引值,可以通過索引值在常量池中找到。

          當(dāng)我們擁有一個(gè)?.class?文件時(shí),我們可以通過?javap?來將字節(jié)碼指令轉(zhuǎn)換為助記符,這個(gè)命令有一些參數(shù),你可以通過?javap -help?來查看所有參數(shù)的說明,這里為了顯示盡量詳細(xì)的內(nèi)容,使用?javap -verbose,其效果如下,但由于內(nèi)容太長,我們不一次性展示所有內(nèi)容,而是分區(qū)域來進(jìn)行閱讀。

          而“方法的屬性”這一部分較為復(fù)雜,直接借助javap -verbose將其反編譯為人可以讀懂的信息進(jìn)行解讀,如下圖所示??梢钥吹綄傩灾邪ㄒ韵氯齻€(gè)部分:

          • ??Code區(qū):源代碼對應(yīng)的JVM指令操作碼,在進(jìn)行字節(jié)碼增強(qiáng)時(shí)重點(diǎn)操作的就是“Code區(qū)”這一部分。 args_size 是參數(shù)數(shù)量,在主函數(shù)中,因?yàn)橛?args 這個(gè)參數(shù),所以在這里 args_size 為 1; locals 是該方法中的本地變量有多少個(gè),在我們的主函數(shù)里面有定義了 3 個(gè)變量,加上一個(gè)參數(shù),因此有 4 個(gè)變量; stack 是方法在執(zhí)行過程中,操作數(shù)棧中最大深度,這個(gè)在之后講解指令執(zhí)行過程時(shí)可以看出。 在這一行信息之后是字節(jié)碼指令,一條指令包括偏移量以及執(zhí)行的指令碼,PC Register 利用偏移量來判斷指令執(zhí)行位置。

          • ??LineNumberTable:行號表,將Code區(qū)的操作碼和源代碼中的行號對應(yīng),Debug時(shí)會(huì)起到作用(源代碼走一行,需要走多少個(gè)JVM指令操作碼)。LineNumberTable:
            ??line?3:?0
            ??line?4:?2
            ??line?5:?4
            ??line?6:?10
            ??line?7:?17?
            ?line 3: 0 代表 Java 源碼文件中的第三行代碼從偏移量為 0 的位置開始,而繼續(xù)往下看可以看到第四行代碼從偏移量為 2 的位置開始,也就是說第三行代碼所對應(yīng)的字節(jié)碼指令有 iconst_1 和 istore_1 兩條。這也可以讓 JVM 執(zhí)行指令出現(xiàn)錯(cuò)誤時(shí),幫助我們定位到對應(yīng)的源碼位置。

          • ??LocalVariableTable:本地變量表,包含This和局部變量,之所以可以在每一個(gè)方法內(nèi)部都可以調(diào)用This,是因?yàn)镴VM將This作為每一個(gè)方法的第一個(gè)參數(shù)隱式進(jìn)行傳入。當(dāng)然,這是針對非Static方法而言。 第一個(gè)屬性 start 為這個(gè)變量可見的起始偏移位置,它的值必須是在Code 中存在的偏移量值。 第二個(gè)屬性 length 為該變量的有效長度,在這個(gè)例子中,我們的變量直到方法末尾都有效,因此你會(huì)發(fā)現(xiàn) start + lenth 的值都是 18 (方法中執(zhí)行的指令數(shù))。當(dāng)我們在一個(gè)局部的代碼塊里面聲明一個(gè)變量,那么它的有效期長度將會(huì)更短。 Slot 為變量在 local variable 中的位置,這可以幫助我們在指令中確定對應(yīng)的變量,而 Name 則是變量名,Signature 為該變量的類型。

          996b1435231e79ece54ca2d86704e808.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          附加屬性表

          字節(jié)碼的最后一部分,該項(xiàng)存放了在該文件中類或接口所定義屬性的基本信息。

          操作數(shù)棧和字節(jié)碼

          JVM的指令集是基于棧而不是寄存器,基于??梢跃邆浜芎玫目缙脚_(tái)性(因?yàn)榧拇嫫髦噶罴陀布煦^),但缺點(diǎn)在于,要完成同樣的操作,基于棧的實(shí)現(xiàn)需要更多指令才能完成(因?yàn)闂V皇且粋€(gè)FILO結(jié)構(gòu),需要頻繁壓棧出棧)。另外,由于棧是在內(nèi)存實(shí)現(xiàn)的,而寄存器是在CPU的高速緩存區(qū),相較而言,基于棧的速度要慢很多,這也是為了跨平臺(tái)性而做出的犧牲。

          我們在上文所說的操作碼或者操作集合,其實(shí)控制的就是這個(gè)JVM的操作數(shù)棧。為了更直觀地感受操作碼是如何控制操作數(shù)棧的,以及理解常量池、變量表的作用,將add()方法的對操作數(shù)棧的操作制作為GIF,如下圖14所示,圖中僅截取了常量池中被引用的部分,以指令iconst_2開始到ireturn結(jié)束。

          23d4e991bdaa466b9debbda9848c1eb7.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)JVM 內(nèi)存結(jié)構(gòu)

          我們的Java程序在運(yùn)行時(shí)是通過?main()?方法啟動(dòng),它是程序的入口,我們的進(jìn)程在啟動(dòng)時(shí)會(huì)為該方法創(chuàng)建一個(gè)主線程來執(zhí)行代碼。當(dāng)我們使用多線程時(shí),那么程序的進(jìn)程將會(huì)擁有多個(gè)線程。每個(gè)線程的資源都擁有獨(dú)自的資源,當(dāng)然它們也可以共享進(jìn)程的資源,那么在 JVM 中,根據(jù)資源的可用范圍,可將內(nèi)存區(qū)域分為線程獨(dú)占和線程共享兩個(gè)類別。JVM內(nèi)存布局

          83acf50ceae5b34e3b44f7584778d492.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          對于每一個(gè)線程,都可將其擁有的內(nèi)存空間分為 PC Register、Native Method Stack、JVM Stack 這3個(gè)區(qū)域,這3個(gè)區(qū)域?qū)τ诰€程來說都是獨(dú)占的,其它線程無法進(jìn)行訪問。

          • ??PC Register?用于記錄當(dāng)前線程指令的執(zhí)行位置。由于一個(gè)進(jìn)程可能有多個(gè)線程,而CPU會(huì)在不同線程之間切換,為了能夠記錄各個(gè)線程的當(dāng)前執(zhí)行的指令,每個(gè)線程都需要有一個(gè) PC Register,來保證各個(gè)線程都可以進(jìn)行獨(dú)立運(yùn)算。

          • ??JVM Stack?用于存放調(diào)用方法時(shí)壓入棧的棧幀。相信學(xué)過數(shù)據(jù)結(jié)構(gòu)的對棧應(yīng)該不陌生,JVM Stack 壓入的單位為棧幀(Frame),用于存儲(chǔ)數(shù)據(jù)、動(dòng)態(tài)鏈接、方法返回值和調(diào)度異常等。每次調(diào)用一個(gè)方法都會(huì)創(chuàng)建一個(gè)新的棧幀壓入 JVM Stack 來存儲(chǔ)該方法的信息,當(dāng)該方法調(diào)用完成時(shí),對應(yīng)的棧幀也會(huì)跟著被銷毀。一個(gè)棧幀都有自己的局部變量數(shù)組、操作數(shù)棧、對當(dāng)前方法類的運(yùn)行常量池的引用。

          • ??Native Method Stack?則是用于調(diào)用操作系統(tǒng)本地方法時(shí)使用的棧空間。

          86bbfa1c204802c5d539e2afdedc5346.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          每個(gè)線程都可用訪問的內(nèi)存空間為線程共享區(qū)域,它包含 Head 和 Method Area 兩個(gè)部分,Head 用于存放實(shí)例對象,也是 GC 回收的主要區(qū)域,而 Method Area 用于存放類結(jié)構(gòu)與靜態(tài)變量。

          現(xiàn)在我們初步了解了 JVM 內(nèi)存的布局,那么接下來可以繼續(xù)看指令的執(zhí)行過程了。

          指令的執(zhí)行過程

          由于 Java 程序從?main()?方法開始,我們也是從這個(gè)方法的指令開始進(jìn)行分析。

          假設(shè)程序運(yùn)行 0 號指令前的狀態(tài)如下,在 mian 方法棧幀里面,有著 operand stack(操作數(shù)棧),它的最大長度為 2(與 Code 下的 stack 的值一致),此外還有一個(gè) local variable(本地變量表)來存放變量的值,其中下標(biāo)為 0 的變量為主方法的參數(shù) args,我們直接用這個(gè)字符串填充在那里來做一個(gè)標(biāo)識(shí)(實(shí)際的值可能是一個(gè)空數(shù)組)。

          f41e0ff79fab5274bbf953b4835443c9.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          接下來我們一步步執(zhí)行方法中的指令,在這里我們先對出現(xiàn)的幾個(gè)指令做一個(gè)簡單的介紹:

          • ??iconst_<i>?放一個(gè) int 常量(-1, 0, 1, 2, 3, 4 or 5) 到 operand stack 中

          • ??istore_<n>?從 operand stack 中獲取一個(gè) int 到 local variable 的 n 中

          • ??iload_<n>?從 local variable 中讀取 int 變量 n 的值到操作數(shù)棧中

          • ??invokestatic?調(diào)用一個(gè) class 的 static 方法

          • ??getstatic?從 class 中獲取一個(gè) static 字段

          • ??invokevirtual?調(diào)用一個(gè)實(shí)例方法,基于類的調(diào)度

          • ??return?從方法中返回一個(gè) void,ireturn?從方法中返回 operand stack 棧頂?shù)?int

          更多的指令與詳細(xì)的說明請查看文章最后參考中的官方指令文檔

          現(xiàn)在我們開始分析指令的執(zhí)行,我們在上面知道了,我們的 Java 代碼所對應(yīng)的指令分別是偏移量為 0 和 1 的兩個(gè),最開始執(zhí)行的是?0: iconst_1,該指令會(huì)把 int 常量 1 放置到 operand stack 中,之后執(zhí)行的是?1: istore_1,把 operand stack 棧頂?shù)?int 常量取出放到 local variable 下標(biāo)為 1 的變量中,該過程圖示如下。

          5f1ec3f016e5dea139f9117ca25ff37e.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          我們可以通過查看 LocalVariableTable 得知下標(biāo)為 1 的變量在我們的 Java 程序中是 int 變量 a,因此上面這兩條指令常量 1 賦值給變量 a。同樣的,后面兩條指令則是將常量 1 賦值給變量 b。這里要注意,操作數(shù)棧的數(shù)是被取出操作,被取出的數(shù)將不會(huì)繼續(xù)在 operand stack 里面。

          執(zhí)行完 0~3 這 4 條指令后,就來到了本例中最為關(guān)鍵的方法調(diào)用了。在執(zhí)行?iload_1?和?iload_2?后,operand stack 中將會(huì)存放著變量 a 和 b 的值,作為?invokestatic?調(diào)用函數(shù)時(shí)傳入的參數(shù)。

          而執(zhí)行到?invokestatic #2?這個(gè)指令的時(shí)候,該指令為調(diào)用一個(gè) class 的 static 方法,也就是調(diào)用常量池中?#2?的方法,該方法為?Hello.add:(II)I?。

          當(dāng)執(zhí)行 invokestatic 時(shí)會(huì)依次讀取 operand stack 的數(shù)據(jù)作為方法的參數(shù),并創(chuàng)建一個(gè)新的棧幀來執(zhí)行方法,將數(shù)據(jù)放到 local variable 對應(yīng)變量位置。

          cdf037547d69af4ed29b293bb8942b84.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          之后開始執(zhí)行?add()?方法中的指令,首先執(zhí)行的是兩個(gè)?iload?指令,將 loca variable 對應(yīng)下標(biāo)的變量的值放到 operand stack 中,之后執(zhí)行?iadd?取出 operand stack 中的值并進(jìn)行加法運(yùn)算,再把結(jié)果放到,最后執(zhí)行 ireturn 取出 operand stack 頂部的 int 值進(jìn)行返回。

          ec0e72cafac7c1156f75b73ba1ce27e6.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)2f8951988cb215b4cf8458ebe238b4e3.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          當(dāng)執(zhí)行完?ireturn?后,add 方法也就執(zhí)行完成了,對應(yīng)的棧幀也會(huì)跟著銷毀。之后回到 main 方法中繼續(xù)往下執(zhí)行,到?istore_3?指令,該指令將棧頂?shù)?int 值取出放到了 local variable 中 Solt 為 3 的地方,這樣執(zhí)行完 4~9 這幾條指令后就完成了我們代碼中的?int c = add(a, b);?這一行代碼。那么接下來就是執(zhí)行?System.out.println(c);?對應(yīng)的指令將 2 打印到控制臺(tái)了。

          到這里其實(shí)我們就已經(jīng)知道如何去閱讀我們代碼生成的 Byte Code 了,這里我就不繼續(xù)往下分析本文例子的代碼了,閱讀過程中如果遇到了沒見過的指令,我們可以在 Oracle 官方指令文檔里面查閱對應(yīng)的說明。

          查看字節(jié)碼工具

          如果每次查看反編譯后的字節(jié)碼都使用javap命令的話,好非常繁瑣。這里推薦一個(gè)Idea插件:jclasslib[1]?。使用效果如圖15所示,代碼編譯后在菜單欄”View”中選擇”Show Bytecode With jclasslib”,可以很直觀地看到當(dāng)前字節(jié)碼文件的類信息、常量池、方法區(qū)等信息。

          54b7800612550dd62e63965cd4c789a9.webpJava字節(jié)碼介紹 - Java技術(shù)債務(wù)

          --------------------------------------歡迎叨擾此地址---------------------------------------

          本文作者:Java技術(shù)債務(wù)?

          原文鏈接:https://cuizb.top/myblog/article/1671634067?

          版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 CC BY 3.0 CN協(xié)議進(jìn)行許可。轉(zhuǎn)載請署名作者且注明文章出處。

          參考
          1. 1.?字節(jié)碼增強(qiáng)技術(shù)探索:https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html

          2. 2.?一文看懂 JVM 內(nèi)存布局及 GC 原理:https://www.infoq.cn/article/3wyretkqrhivtw4frmr3

          3. 3.?Oracle 官方說明文檔:https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-4.html#jvms-4.10

          4. 4.?Oracle 官方指令文檔:https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-6.html

          引用鏈接

          [1]?jclasslib:?https://plugins.jetbrains.com/plugin/9248-jclasslib-bytecode-viewer



              
                  請各位看官大人,多點(diǎn)點(diǎn)贊、在看以及原文。給小的一個(gè)鼓勵(lì),謝謝您。。。
                
              

          更多請移駕

          ??????????????????????????????????

          本文作者:Java技術(shù)債務(wù)?

          原文鏈接:https://cuizb.top/myblog/article/1671634067?

          版權(quán)聲明:?本博客所有文章除特別聲明外,均采用 CC BY 3.0 CN協(xié)議進(jìn)行許可。轉(zhuǎn)載請署名作者且注明文章出處。

          ??????????????????????????????????

                
                  
                    
                              

          0dc0621dd9431d941c2f14cf8c4d6441.webp

                
                  
                    
                              
                                

          JVM內(nèi)存泄漏和內(nèi)存溢出的原因
          JVM常用監(jiān)控工具解釋以及使用

          Redis 常見面試題(一)

          ClickHouse之MaterializeMySQL引擎(十)

          三種實(shí)現(xiàn)分布式鎖的實(shí)現(xiàn)與區(qū)別

          線程池的理解以及使用

                                  


          號外!號外! 最近面試BAT,整理一份面試資料 ,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。想獲取嗎? 如果你想提升自己,并且想和優(yōu)秀的人一起進(jìn)步,感興趣的朋友, 可以在 掃碼關(guān) 注下方 公眾號 。資料在公眾號里靜靜的躺著呢。。。

          喜歡就 分享
          認(rèn)同就 點(diǎn)贊

          支持就 在看

          一鍵四連,你的offer也四連 bd4d5dd75c00c7c746b068af98484bc7.webp


          瀏覽 55
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91人操| 五月天成人在线观看 | 手机av网站大全 手机在线性爱视频 | 秋霞午夜电影 | 囯产一级持黄视频 |