javascript
0.07 秒启动一个 SpringBoot 项目!
寫一段簡單的 Java 程序。
通常我們想運行它要這樣。
[root@flash?~]#?javac?Hello.java [root@flash?~]#?java?Hello hello?world但運行起來需要 jre。
我們換一種方式來編譯這個程序,首先下載一個 GraalVM 的 native-image 工具,然后。
[root@flash?~]#?native-image?Hello [hello:11725]????classlist:???1,031.19?ms,??0.96?GB [hello:11725]????????(cap):???2,624.14?ms,??0.96?GB [hello:11725]????????setup:???3,960.95?ms,??0.96?GB [hello:11725]?????(clinit):?????288.49?ms,??1.72?GB [hello:11725]???(typeflow):???2,642.38?ms,??1.72?GB [hello:11725]????(objects):???3,803.54?ms,??1.72?GB [hello:11725]???(features):???1,176.79?ms,??1.72?GB [hello:11725]?????analysis:???8,288.82?ms,??1.72?GB [hello:11725]?????universe:?????909.14?ms,??1.75?GB [hello:11725]??????(parse):?????801.67?ms,??1.75?GB [hello:11725]?????(inline):???1,096.07?ms,??2.32?GB [hello:11725]????(compile):???7,352.50?ms,??2.37?GB [hello:11725]??????compile:??10,146.59?ms,??2.37?GB [hello:11725]????????image:???1,639.93?ms,??2.37?GB [hello:11725]????????write:?????682.24?ms,??2.37?GB [hello:11725]??????[total]:??26,855.67?ms,??2.37?GB #?Printing?build?artifacts?to:?.../hello.build_artifacts.txt執行完這個命令后,發現當前目錄多了個 hello 文件。
直接執行它,可以成功!
[root@flash?~]#?./hello hello?world而且注意,這個是可以直接以二進制形式運行的,不依賴 jre。
也就是說,一個 Java 程序,被這個 native-image 編譯成了本地代碼!
這項技術來自于 GraalVM 的一個特性,在其官網的文檔中可以了解到,GraalVM 主要有三大特性:
通過新的 JIT 技術使 Java 程序更快運行
多語言支持
構建 JVM 無關的本地鏡像
這個 native-image 技術就是其中的第三點,即將 Java 代碼編譯成 JVM 無關的本地鏡像,使其可以直接以二進制的方式運行起來。
除了運行方便之外,我們對比一下這倆的文件大小和啟動時間。
[root@flash?~]#?ll -rw-r--r--??1?flash??staff???415B?10?27?15:50?Hello.class -rwxr-xr-x??1?flash??staff????10M?10?27?15:51?hello[root@flash?~]#?time?java?Hello hello?world java?Hello??0.09s?user?0.03s?system?113%?cpu?0.106?total[root@flash?~]#?time?./hello hello?world ./hello??0.00s?user?0.01s?system?34%?cpu?0.032?total總結個表格。
Hello.class??415B? 0.12s
hello? 10M? 0.01s
可以看出,啟動時間大大縮短了!但文件大小卻大大增加了。
不過要知道,運行 Hello.class 要整個 jre 的支持,而運行二進制的 hello 卻不需要,這部分文件大小的差距,在小代碼上對比并不公平。
當然,啟動時間也都是毫秒級的,差距也不足以說明問題。下面我們試著用這種方式,對比一個 Spring Boot 項目。
有一點要說明的是,GraalVM 的本地編譯對 Java 代碼有很多的限制,有的時候需要配合配置文件才能成功,比如不支持動態類加載、反射、序列化等,具體可以見這里:
https://www.GraalVM.org/reference-manual/native-image/Limitations/
可是 Spring 項目中可是大量充斥著這些,我們需要增加好多配置文件,才能成功本地編譯一個 Spring Boot 項目。
好在,Spring 已經為我們考慮好這些事情了,提供了一個專門為 native 而生的 Spring Boot 依賴項,最方便的是我們新建項目的時候可以直接從 start.spring.io 生成。
然后可以直接用 mvn 命令來打包一個本地鏡像。
[root@flash?~]#?mvn?package?-Pnative ... [INFO]?Executing:?...native-image?-cp?...?-H:Name=demo-1 ... [demo-1:7725]????classlist:???1,695.81?ms,??0.94?GB [demo-1:7725]????????(cap):???1,932.48?ms,??0.94?GB [demo-1:7725]????????setup:???3,287.65?ms,??0.94?GB [demo-1:7725]?????(clinit):???2,256.61?ms,??5.68?GB [demo-1:7725]???(typeflow):??18,462.41?ms,??5.68?GB [demo-1:7725]????(objects):??17,848.47?ms,??5.68?GB [demo-1:7725]???(features):???4,646.24?ms,??5.68?GB [demo-1:7725]?????analysis:??45,521.71?ms,??5.68?GB [demo-1:7725]?????universe:???2,624.03?ms,??5.68?GB [demo-1:7725]??????(parse):???1,917.71?ms,??5.68?GB [demo-1:7725]?????(inline):???6,021.71?ms,??5.93?GB [demo-1:7725]????(compile):??30,497.99?ms,??6.06?GB [demo-1:7725]??????compile:??42,184.66?ms,??6.06?GB [demo-1:7725]????????image:???8,700.31?ms,??5.90?GB [demo-1:7725]????????write:???1,647.51?ms,??5.90?GB [demo-1:7725]??????[total]:?106,412.95?ms,??5.90?GB #?Printing?build?artifacts?to:?.../demo-1.build_artifacts.txt同樣,我們用傳統的 jar 包方式打包一個 jar 文件,對比一下。
這回大小已經沒差那么多了,但仍然是二進制的本地包大。不過這僅僅是幾乎空的 Spring Boot 項目,隨著項目依賴的包越來越多,二進制的文件大小會越來越有優勢,這是后話了。
我們再來對比一下啟動速度,首先是傳統的 jar 包運行。
[root@flash?~]#?java?-jar?demo-1-exec.jar?.???____??????????_????????????__?_?_/\\?/?___'_?__?_?_(_)_?__??__?_?\?\?\?\ (?(?)\___?|?'_?|?'_|?|?'_?\/?_`?|?\?\?\?\\\/??___)|?|_)|?|?|?|?|?||?(_|?|??)?)?)?)'??|____|?.__|_|?|_|_|?|_\__,?|?/?/?/?/=========|_|==============|___/=/_/_/_/::?Spring?Boot?::????????????????(v2.5.6)2021-11-02?16:36:11.192??INFO?9468?---?[main]?com.example.demo1.Demo1Application???????:?Starting?Demo1Application?v0.0.1-SNAPSHOT?using?Java?11.0.12?on?sunyiming07deMacBook-Pro.local?with?PID?9468?(/Users/sunyiming07/IdeaProjects/graalvm-demos/springboot/demo/demo-1/target/demo-1-0.0.1-SNAPSHOT-exec.jar?started?by?sunyiming07?in?/Users/sunyiming07/IdeaProjects/graalvm-demos/springboot/demo/demo-1/target) 2021-11-02?16:36:11.195??INFO?9468?---?[main]?com.example.demo1.Demo1Application???????:?No?active?profile?set,?falling?back?to?default?profiles:?default 2021-11-02?16:36:12.097??INFO?9468?---?[main]?o.s.b.w.embedded.tomcat.TomcatWebServer??:?Tomcat?initialized?with?port(s):?8080?(http) 2021-11-02?16:36:12.110??INFO?9468?---?[main]?o.apache.catalina.core.StandardService???:?Starting?service?[Tomcat] 2021-11-02?16:36:12.110??INFO?9468?---?[main]?org.apache.catalina.core.StandardEngine??:?Starting?Servlet?engine:?[Apache?Tomcat/9.0.54] 2021-11-02?16:36:12.164??INFO?9468?---?[main]?o.a.c.c.C.[Tomcat].[localhost].[/]???????:?Initializing?Spring?embedded?WebApplicationContext 2021-11-02?16:36:12.164??INFO?9468?---?[main]?w.s.c.ServletWebServerApplicationContext?:?Root?WebApplicationContext:?initialization?completed?in?917?ms 2021-11-02?16:36:12.484??INFO?9468?---?[main]?o.s.b.w.embedded.tomcat.TomcatWebServer??:?Tomcat?started?on?port(s):?8080?(http)?with?context?path?'' 2021-11-02?16:36:12.494??INFO?9468?---?[main]?com.example.demo1.Demo1Application???????:?Started?Demo1Application?in?2.033?seconds?(JVM?running?for?2.504)2.033 秒,已經慢下來了,不過正常的空 Spring Boot 項目也就這樣。
再看看本地鏡像啟動速度。
[root@flash?~]#?./demo-1 2021-11-02?16:38:33.141??INFO?9724?---?[main]?o.s.nativex.NativeListener???????????????:?This?application?is?bootstrapped?with?code?generated?with?Spring?AOT.???____??????????_????????????__?_?_/\\?/?___'_?__?_?_(_)_?__??__?_?\?\?\?\ (?(?)\___?|?'_?|?'_|?|?'_?\/?_`?|?\?\?\?\\\/??___)|?|_)|?|?|?|?|?||?(_|?|??)?)?)?)'??|____|?.__|_|?|_|_|?|_\__,?|?/?/?/?/=========|_|==============|___/=/_/_/_/::?Spring?Boot?::????????????????(v2.5.6)2021-11-02?16:38:33.143??INFO?9724?---?[main]?com.example.demo1.Demo1Application???????:?Starting?Demo1Application?v0.0.1-SNAPSHOT?using?Java?11.0.12?on?sunyiming07deMacBook-Pro.local?with?PID?9724?(/Users/sunyiming07/IdeaProjects/graalvm-demos/springboot/demo/demo-1/target/demo-1?started?by?sunyiming07?in?/Users/sunyiming07/IdeaProjects/graalvm-demos/springboot/demo/demo-1/target) 2021-11-02?16:38:33.143??INFO?9724?---?[main]?com.example.demo1.Demo1Application???????:?No?active?profile?set,?falling?back?to?default?profiles:?default 2021-11-02?16:38:33.178??INFO?9724?---?[main]?o.s.b.w.embedded.tomcat.TomcatWebServer??:?Tomcat?initialized?with?port(s):?8080?(http) 2021-11-02?16:38:33.178??INFO?9724?---?[main]?o.apache.catalina.core.StandardService???:?Starting?service?[Tomcat] 2021-11-02?16:38:33.178??INFO?9724?---?[main]?org.apache.catalina.core.StandardEngine??:?Starting?Servlet?engine:?[Apache?Tomcat/9.0.54] 2021-11-02?16:38:33.184??INFO?9724?---?[main]?o.a.c.c.C.[Tomcat].[localhost].[/]???????:?Initializing?Spring?embedded?WebApplicationContext 2021-11-02?16:38:33.184??INFO?9724?---?[main]?w.s.c.ServletWebServerApplicationContext?:?Root?WebApplicationContext:?initialization?completed?in?41?ms 2021-11-02?16:38:33.204??INFO?9724?---?[main]?o.s.b.w.embedded.tomcat.TomcatWebServer??:?Tomcat?started?on?port(s):?8080?(http)?with?context?path?'' 2021-11-02?16:38:33.204??INFO?9724?---?[main]?com.example.demo1.Demo1Application???????:?Started?Demo1Application?in?0.078?seconds?(JVM?running?for?0.08)我去!0.078 秒!!!我還真從來沒有啟動 Spring Boot 項目體驗過這么極速的狀態呢!!!容我高興一會兒。
看吧,前面的 hello world 項目看不出什么,現在的 Spring Boot 項目,優勢就已經完全出來了,啟動速度秒殺呀!
可想而知,我們原來啟動可能要幾分鐘才成功的 Spring Boot 項目,會被這個 GraalVM 優化到多少呢?想想就激動!
不過這個我還沒有試,光是跑這個 Spring Boot 空項目就忙活了好久,一直報各種各樣奇怪的錯誤,等我再熟練熟練的。
剛剛也說了,想通過 GraalVM 的 native-image 功能編譯一個 Java 程序,有很多限制,比如不支持動態類加載、反射、動態代理、JNI、序列化以及 invoke dynamic 指令等。
這是由于,AOT 這種提前編譯的技術,需要一個封閉空間假設,即在編譯期就能夠把運行期所有需要的東西都準備好,但 Java 的好多特性就是和這種封閉空間假設相沖突的。
Java 啟動后隨著程序不斷運行,JVM 將一部分代碼編譯成本地代碼,這個叫 JIT 技術,它是在程序運行起來之后不斷分析而做的編譯,所以它不受封閉空間假設的限制。
說回 GraalVM 的 AOT,比如程序中有個反射,這就屬于運行時才會知道有這樣一個 Student 類被需要的情況。
Class.forName("com.flash.Student")當然,GraalVM 會通過掃描這些反射方法的調用,來嘗試分析用到了哪些類。
如果分析不出來,就需要程序員手動配置,告訴 GraalVM 有哪些類要反射。
[{name:?"com.flash.Student",allDeclaredConstructors:?true,allPublicMethods:?true},{name:?"com.flash.Teacher",fileds:?[{name:?"teach"},?{name:?"talk"}],methods:?[{name:?"<init>",parameterTypes:?["char[]"]}]},//?…… ]但這樣肯定是反人性的。
自己寫的代碼和依賴還好,但如果是使用第三方組建,比如人人都用的 Spring,肯定不能由程序員來去寫這些配置文件。
那就只有讓 Spring 官方提供這些配置,讓程序員仍然是簡單寫一些 maven 依賴就能把項目跑起來,才能把這個技術推廣出去,這也是剛剛 Spring Native 項目存在的意義。
今天簡單給大家分享下 GraalVM 的使用,這個技術基本還沒有公司大規模在用,還達不到工業級的成熟,不過未來云原生領域要求小包和快速啟動兩個特性,GraalVM 的未來說不定有大舞臺呢。
總結
以上是生活随笔為你收集整理的0.07 秒启动一个 SpringBoot 项目!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 惹毛了老婆后,老王居然本能地想按Ctrl
- 下一篇: Java8使用Stream的缺点是调试困