Java之編譯期優化

2020-12-11 不知名產品與技術

概述

Java語言的「編譯期」其實是一段「不確定」的操作過程,因為他可能是指一個前端編譯器把java文件轉變成class文件的過程;也可能是指虛擬機的後端運行期編譯器把字節碼轉變成機器碼的過程;還可能是指使用靜態提前編譯器直接把java文件編譯成本地機器代碼的過程。以下是這三類比較有代表性的編譯器:

前端編譯器:Sun的javac、Eclipse JDT中的增量式編譯器。

JIT編譯器:HotSpot VM的C1、C2編譯器。

AOT編譯器:GNU Compiler for the Java (GCJ)、Excelsior JET。

Javac這類編譯器對代碼的運行效率幾乎沒有任何優化措施。虛擬機設計團隊把對性能的優化集中到了後端的即時編譯器中,這樣可以讓那些不是由Javac產生的Class文件(如JRuby、Groovy等語言的Class文件)也同樣能享受到編譯器優化所帶來的好處。但是,相當多新生的Java語法特性,都是靠編譯器的「語法糖」來實現,而不是依賴虛擬機的底層改進來支持,可以說,Java中即時編譯器在運行期的優化過程對於程序運行來說更重要,而前端編譯器在編譯器的優化過程對於程序編碼來說關係更加密切。

Javac編譯器

Javac本身就是一個由Java語言編寫的程序,這為純Java的程式設計師了解它的編譯過程帶來了很大的便利。

Javac的源碼與調試

從Sun Javac的代碼來看,編譯過程大致可以分為3個過程,分別是:

解析與填充符號表過程。

插入式註解處理器的註解處理過程。

分析與字節碼生成過程。

解析與填充符號表

解析步驟包括了經典程序編譯原理中的詞法分析和語法分析兩個過程。

1.詞法、語法分析

詞法分析是將原始碼的字符流轉變為標記集合,單個字符是程序編寫過程的最小元素,而標記則是編譯過程的最小元素。在Javac的源碼中,詞法分析過程由com.sun.tools.javac.parser.Scanner類來實現。

語法分析是根據Token序列構造抽象語法樹的過程,抽象語法樹是一種用來描述程序代碼與法結構的樹形表示方式,語法樹的每一個節點都代表著程序代碼中的一個語法結構。語法分析過程由com.sun.tools.javac.parser.Parser類實現。

2.填充符號表

完成了語法分析和詞法分析之後,下一步就是填充符號表的過程。符號表中所登記的信息在編譯的不同階段都要用到。在語義分析中,符號表所登記的內容將用於語義檢查和產生中間代碼。在目標代碼生成階段,當對符號名進行地址分配時,符號表是地址分配的依據。在Javac原始碼中,填充符號表的過程由com.sun.tools.javac.comp.Enter類實現。

註解處理器

在JDK1.5之後,Java語言提供了對註解的支持,這些註解與普通的Java代碼一樣,是在運行期間發揮作用的。

語義分析與字節碼生成

語義分析的主要任務是對結構上正確的源程序進行上下文有關性質的審查,如進行類型審查。

1.標註檢查

語義分析過程分為標註檢查以及數據及控制流分析兩個步驟。標註檢查步驟檢查的內容包括諸如變量使用前是否已被聲明、變量與賦值之間的數據類型是否能夠匹配等。

2.數據及控制流分析

數據及控制流分析是對程序上下文邏輯更進一步的驗證,他可以檢查出諸如程序局部變量在使用前是否有賦值、方法的每條路徑是否都有返回值、是否所有的受查異常都被正確處理了等問題。將局部變量聲明為final,對運行期是沒有影響的,變量的不變性僅僅由編譯器在編譯期間保障。

3.解語法糖

語法糖也稱糖衣語法,指在計算機語言中添加的某種語法,這種語法對語言的功能並沒有影響,但是更方便程式設計師使用。通常來說,使用語法糖能夠增加程序的可讀性,從而減少程序代碼出錯的機會。

Java中最常用的語法糖主要是前面提到過的泛型、變長參數、自動裝箱、拆箱等,虛擬機運行時不支持這些語法,它們在編譯階段還原回簡單的基礎與法結構,這個過程稱為解語法糖。

4.字節碼生成

字節碼生成是Javac編譯過程的最後一個階段,在Javac源碼裡面由com.sum.tools.javac.jvm.Gen類來完成。字節碼生成極端不僅僅是把前面各個步驟所生成的信息(語法樹、符號表)轉化成字節碼寫到磁碟中,編譯器還進行了少量的代碼添加和轉換工作。

Java語法糖的味道

幾乎各種語言或多或少都提供過一些語法糖來方便程式設計師的代碼開發,這些語法糖雖然不會提供實質性的功能改進,但是它們或能夠提高效率,或能提醒語法的嚴謹性,或能減少編碼出錯的機會。

泛型與類型擦除

自動裝箱、拆箱與遍歷循環

條件編譯

相關焦點

  • Java編譯期與運行期,別傻傻分不清楚!
    想通過這篇文章來分析分析Java的執行流程,或者換句話說想聊聊Java的編譯期與運行期的流程。1. 開門見山上面只是大概講了運行一個java程序的流程,下面再從編譯期以及運行期的角度在剖析一下細節。2. 編譯期間都做了什麼?編譯器(compiler)是一種電腦程式,它會將某種程式語言寫成的原始碼(原始語言)轉換成另一種程式語言(目標語言)。
  • Java之使用泛型與未使用泛型的區別
    各位小夥伴們大家好,在之前的文章中,小編有介紹過Java之泛型的概念,這次小編要介紹的是,Java程序中,使用泛型與未使用泛型的區別。public class Demo01Generic {public static void main(String[] args) { show01(); show02(); } /*創建集合對象,使用泛型好處:1.避免了類型轉換的麻煩,存儲的是什麼類型的數據,取出的就是什麼類型的數據 2.把運行期異常(代碼運行之後會拋出的異常),提升到了編譯期
  • Java的「泛型」特性,你以為自己會了?(萬字長文)
    # 泛型的引入,是java語言的來講是一個較大的功能增強。同時對於編譯器也帶來了一定的增強,為了支持泛型,java的類庫都做相應的修改以支持泛型的特性。(科普:實際上java泛型並不是 jdk5(2004發布了jdk5) 才提出來的,早在1999年的時候,泛型機制就是java最早的規範之一)另外,泛型還具有以下的優點:# 1.提交了ja
  • 軟體特攻隊|針對C++編譯期多態與運行期多態,我有話說
    在C++的面向對象編程中,多態是OO三大特性之一,我們稱為運行期多態,也稱它為動態多態;但在泛型編程中,多態是基於模板的具現化與函數的重載解析,由於這種多態發生於編譯期,所以稱它為編譯期多態或靜態多態。運行期多態運行期多態的設計來源於類繼承體系的設計上。
  • java開發之Tomcat線程池優化
    前言:上期我們說到了jvm的內存優化,這期我們來說說tomcat的線程池優化,此思路同樣可用於c3p0等連接池tomcat線程池優化我們tomcat線程池的優化,其實就是最大限度的發揮tomcat的性能。即讓伺服器在保障性能的情況下並發最大並發:所有線程,在同一秒一起訪問同一個資源。
  • 雲原生時代,Java的危與機
    Java 的危機Java 與雲原生的矛盾,來源於 Java 誕生之初,植入到它基因之中的一些基本的前提假設已經逐漸開始被動搖,甚至已經不再成立。我舉個例子,每一位 Java 的使用者都聽說過「一次編寫,到處運行」(Write Once, Run Anywhere)這句口號。
  • 關於Java泛型,一個95%的人都搞不清的問題
    與之相反,泛型是逆變的:對於任意兩個不同的類型Type1與Type2來說,List<Type1>既不是List<Type2>的子類型,也不是其父類型[JLS, 4.10; Naftalin07, 2.5]。你可能覺得這意味著泛型是有缺陷的,不過有缺陷的卻是數組。如下代碼片段是合法的:// Fails at runtime!
  • Java新特性:數據類型可以扔掉了?
    所以使用 enum 關鍵字定義常量,儘管從 Java 語法上看起來與使用 class 關鍵字定義類、使用 interface 關鍵字定義接口是同一層次的,但實際上這是由 Javac 編譯器做出來的假象,從字節碼的角度來看,枚舉僅僅是一個繼承於 java.lang.Enum、自動生成了 values() 和 valueOf() 方法的普通 Java 類而已,因此枚舉也歸為引用類型了。
  • Java典型面試題 ——談談你對Java平臺的理解?
    眾所周知,我們通常把 Java 分為編譯期和運行時。這裡說的 Java 的編譯和 C/C++ 是有著不同的意義的,Javac 的編譯,編譯 Java 源碼生成「.class」文件裡面實際是字節碼,而不是可以直接執行的機器碼。Java 通過字節碼和 Java 虛擬機(JVM)這種跨平臺的抽象,屏蔽了作業系統和硬體的細節,這也是實現「一次編譯,到處執行」的基礎。
  • Java 數據持久化系列之 HikariCP
    更加遵循 JDBC 規範,在關閉 Connection 之前先關閉與之關聯的 Statement 和ResultSet 等。對 JDBC 不了解的同學可以閱讀本系列中第一篇文章。對於資料庫連接中斷的情況,HikariCP 也處理的更加出色。
  • Java泛型的特點與優缺點,泛型擦除是怎麼回事?
    list.getClass().getMethod("add", Object.class); add.invoke(list,"a"); System.out.println(list); System.out.println(list.get(1)); }這就證明了雖然 List 聲明為只能裝 Integer 類型,但是我卻放進了字符串類型,由此可證,泛型是「假」的,只存在於編譯期
  • 十分鐘搞懂Java效率工具Lombok使用與原理
    我個人覺得 Lombok是一個優化Java代碼以及提升開發效率不錯的工具。
  • java技能提升,用Lombok甩掉get和set,讓代碼變得更簡潔
    現實中有說許多看到密密麻麻的get()和set()就會頭疼,但現在有了Lombok,它提供了簡單的註解的形式來幫助我們簡化消除一些必須有但顯得很臃腫的 java 代碼。通過使用對應的註解,可以在編譯源碼的時候生成對應的方法,所以不會影響任何運行效率。
  • 最常見的10種Java異常問題!
    1.檢查型異常(checked) vs 非檢查型異常(Unchecked)簡單來說,對於檢查型異常, 一般在 編譯期 就會被檢查到,所以我們肯定會提前在方法內進行捕獲處理,或者在方法頭部申明並拋出。而非檢查型異常,往往無法提前預知,例如被除數是0、空指針等。檢查型異常特別重要,它會告訴那些調用你的接口的開發者們,如何提前預知並處理好這些可能發生的異常。
  • Java 中的 String 有沒有長度限制?
    根據Integer類的定義,java.lang.Integer#MAX_VALUE的最大值是2^31 - 1;那麼,我們是不是就可以認為String能支持的最大長度就是這個值了呢?其實並不是,這個值只是在運行期,我們構造String的時候可以支持的一個最大長度,而實際上,在運行期,定義字符串的時候也是有長度限制的。
  • Java內存分配和String類型的深度解析
    一、引題在java語言的所有數據類型中,String類型是比較特殊的一種類型,同時也是面試的時候經常被問到的一個知識點,本文結合java內存分配深度分析關於String的許多令人迷惑的問題。下面是本文將要涉及到的一些問題,如果讀者對這些問題都了如指掌,則可忽略此文。1、java內存具體指哪塊內存?這塊內存區域為什麼要進行劃分?是如何劃分的?
  • Java性能優化的50個細節(珍藏版)
    在JAVA核心API中,有許多應用final的例子,例如java、lang、String,為String類指定final防止了使用者覆蓋length()方法。另外,如果一個類是final的,則該類所有方法都是final的。java編譯器會尋找機會內聯(inline)所有的final方法(這和具體的編譯器實現有關),此舉能夠使性能平均提高50%。
  • java基礎案例之java語言組成和數據類型與強制類型轉換語法
    java語言組成包含:1:關鍵字,2:標識符。3:注釋。4:常量和變量。5:運算符 。 6:語句 7:函數 8:數組。java數據類型定義變量格式:數據類型 變量名 =初始化值;例如:int x =4;
  • java基礎案例之java語言流程控制if,if else, if elseif else
    java流程控制語句中判斷結構是由if引導的。點擊關注或收藏,與我們一起從基礎開始學習java每天更新一點新知識,新案例,每天吸收一點新能量。點擊評論區,對於細節的優化,留言,一起探討喲。明日更新程序流程控制之選擇結構語句案例
  • Java 中的語法糖
    其實這背後的原理是編譯器做了優化。如下面代碼,將基本類型賦值給包裝類其實是調用了包裝類的valueOf()方法創建了一個包裝類再賦值給了基本類型。而包裝類賦值給基本類型就是調用了包裝類的xxxValue()方法拿到基本數據類型後再賦值的。