1、JAVA語言的特點
JAVA語言的特點如下圖可以看到企業級性能好,穩定,生態豐富,JavaEE的標準非常好,後面還有跨平臺的特性,按層次來看 Java平臺,Java硬件上是arm或者x86或者PowerPC等等不同CPU,在此之上操作系統是如windows、Linux、Mac OS,再往上是JAVA platform,是一個平臺,因為只要根據 Java平臺來寫應用,可以完全不管操作系統以及硬件的一些具體實現細節,跨平臺性是Java的一個主要賣點,在此之上Java定義了 Java1g2e的標準,然後Tom Kate、APACHE等各種組件其實都是從JAVA EE的標準裡面衍生出來的,幫助Java很好的成長,按照 Java一開始設計的人目標去成長。
生態豐富的特點,因為Java上有Tom Kate、APACHE 、spring等各種生態,開箱即用,開發應用非常簡單;企業級性能好,Java它適合於運行一種大型、長期運行的程序,穩定性非常好,如運行一個Linux server加 JVM的一個組合,可能幾年都不用重啟,可以跑得非常好;
2、一次編譯到處執行
一次編譯到處執行是Java的一個很大的賣點,通過一段代碼來了解在Java內是如何被加載和執行的,如在應用代碼裡面去調用 new Gson(). from Json(..);然後有一個new byte code會觸發loadClass()機制,還要去找”com.google.gson.Gson”用loadClass方法,去找Jar,因為應用的class 下有很多Jar包,如commons-io.jar、myaql-connector.jar、gson.jar,找到gson.jar裡面有com.google/gson/Gson.class文件,然後會把 Class給讀出來,讀成一個byte的數組,調用一個define class JVM的接口,define class會進行parse、verify、link,調出<clinit>,最終達到一個可以讓Jvm識別的 byte code,Jvm解釋器會去執行byte code到2000次以後,會運行一個client compiler讓代碼編譯到c1級別,c1級別其實已經在native執行了,同時會收集一些provide信息,幫助編譯到更高的優化級別c2,然後到15,000次以後會進入到最快的c2級別,interpret和c2之間可能大概有50倍的差距,所以Java1開始是很慢的,但只要跑穩後是非常快的。
通過上述,一段代碼想要被執行,生命週期是非常長的,優點是這種跨平臺性,可以收集的信息越跑越快,缺點就是Java代碼裝載的開銷非常大。
3、內存管理
Java的垃圾回收管理GC, heap就是Java裡面實際做管理的內存,是虛擬內存,一開始如設4Gheap,會想到操作系統,申請4G的heap,這些配置其實都沒有被申請出來,是按需分配的,這是操作系統的推薦機制,隨著各種new Objiect,頁面就會被分配出來,如下圖所示分為9個頁面,就是RSS=9,中間如果發生garbage collection,即便會壓縮內存的,如這裡面有6個頁面是空閒的,他就把三個排到一起,然後剩餘的內存是空閒的,但是操作系統認為依然佔著這些內存,然後它是不可用的,所以RSS依然很高,因為內存很有可能隨著後面的分配馬上會被使用,Java也是積攢到一定程度來釋放的,所以很有可能佔據很大的面積。
4、線程模型
用Java去寫server端應用的時有很多框架,如Tom Kate後面可能會有JPA、Hibernate ORM 、JDBC等組件是通過一個NIO接收請求,接收到請求以後是交到一個thread pool,thread pool多線程可能並不是一種特別高效的處理併發的模型,下圖可以看到一個g2e的規範,在thread的規範裡面,一個外部容器怎樣去處理併發的請求是通過在多線程裡面去並行的調用service函數來達到的,標準導致這個事件必須通過線程池來提供併發能力。
線程多會導致的問題,上圖是一張實際的執行圖,每一個豎條代表一個線程,每一個色塊代表一個請求,如在只有一個核的機器上去啟動4個線程,其實操作系統提供能力,會覺得請求是併發並行來執行的,實際上都是交替的執行分時複用的,看起來是併發的,實際是交替來執行,這中間就有一個切換開銷是比較大的,是多線程的一個弊端。
5、雲原生vsJava
總結了Java的優勢,雲原生的應用應該是微服務的,但Java是一個企業級的模式都非常龐大;雲原生是低內存開銷,這樣可以創造很多微服務,把他們更快的合併更好的和部署,但Java是GC管理是大塊內存的;雲原生要求應用是快速交付,Java應用啟動慢,需要編譯預熱,中間是有一個牴觸的,
6、Dragonwell
所有的市面上的JDK產品大多都基於Open JDK, Open JDK加上 oracle的一些商業特性,然後形成的是oracle JDK,其它還有一些三方廠商,如說亞馬遜等都通過Open JDK進行了簡單的擴展,形成自己的JDK,阿里雲也是通過Open JDK作為上游擴展,然後加上阿里雲的一些原生特性,最終形成了阿里巴巴Dragonwell這個產品。
7、Dragonwell:Elastic Heap
阿里雲自己的一些特性也會迴歸社區,這裡介紹Dragonwell的幾個主要特性,第一個是彈性堆,就是可以把前面看到的一些那問題給解決掉,把內存還給操作系統;往下是進程級別,然後 Java應用級別,然後裡面有JavaHeap,Java heap裡面之間內存默認都是固定的,但是通過Elastic Heap機制,可能用不到這麼多內存就可以把應用跑得很好,會動態的去減少Heap大小,把內存實際還給操作系統。
下圖右半部為特性的使用場景,可能有一些在線的job,比如4個應用,平時用戶量很大,需要大量內存去應付很多請求,所以內存是不共享的,但是到晚上可能沒有很多用戶在訪問在線應用了,就可以動態的把內存給釋放出來,然後提供給其他一些同級以上的離線服務,離線服務和在線服務可以捆綁,或者是複用內存,能達到一個很好的資源複用的目的。
8、Dragonwell:wisp
還有一個非常有用的特性叫做wisp協程,這個特性可以把Java的線程映射為協程,去提高Java的併發處理能力,因為目前都是微服務的狀態,微服務把應用拆掉之後,自然不同服務就要通信,通信的就會很多wisp提高這些io的效率。
現在 Java有很多異步編程框架,如 VERT.X這個詞的含義就是節點,跟node的jS在圖算法裡面其實是可以互相代替使用的,VERT.X和 node.js就是想製造一種Java裡的node,希望用node的這種異步編程模型得帶給Java。
下圖是Verte.x編寫一個數據庫的訪問應用,用他官方的一個API:client.getConnection到這裡不是直接的返回的connection,而是寫一個call back,因為這是一個組詞函數,異步的他就要寫call back,然後call back裡面去判斷請求是否成功,成功的話可以拿到result裡面一個connection,然後Connection去查一個SQL語句,比如“select*FROM...”,這裡同樣要寫一個call back result to,而不是說直接拿到 connection,一層層的回調,如果我們要繼續result再去經營什麼,比如說把它放到緩存裡面,那裡面又會進行一層嵌套的回調。
這樣代碼的控制就被反轉了,並且異常也看不到,如在某一層發生一個異常,因為都是從回調直接執行的,是看不到從哪調進來的,這是異步編程帶給我們一個問題,當然異步編程是有解決方法的。
C sharp和ES7所提供的新特性,主要是提供了協程機制,Kotlim程序作為例子,來解釋這種機制如何幫助去改變義務變成模型,在Kotlim裡面,如果想讓一段代碼可以被協程切換的話,可以用suspend標記這段代碼,然後可以對一個異步函數進行封裝,如client.getconnection,把它封裝成client.aGet connection,實際做調用了一個非方法叫suspendCoroutine做的是調用這一段代碼,並且把當前協程切走,會馬上調用 getconnection,並且得到一個continuation的回調,在 get connection的call back,如get connection實際完成的時候,會恢復協程馬上掛起,然後再完成恢復,這樣就可以讓代碼臨時掛起,過一段時間再回來執行,只有協程可以帶給的優勢,整個執行站都是保留完整的,通過這種封裝可以這樣去寫代碼;Conn=clientaGetconnection,rs=connection=,aQuery同樣要進行包裝。
這樣可以達到異步的一個性能,同時使用完全阻塞方式去寫這個代碼,非常高效, C sharp的協程的一個解決方案,但是這裡對這些帶Kotlim的進行封裝,其實是非常繁瑣的。
9、Dragonwell:wisp原理
wisp其實就是把所有這些需要封裝註冊函數全部可以做到JDK,可以看到像 g.u.c等,其實都被wisp做了像上述的封裝,會切走協程,等到事件ready的時候再把這協程切回來,用戶不用關心。
以前阻塞的API是支持的完全不需要改代碼,可以把以前的用協程寫代碼直接切換到協程模型,進行一個模型轉換,這樣從java thread和Pthread就操作系統1:1的模型變到調用大量wisp變成少量操作統線程,性能大大提高。
10、Dragonwell:JWarmup
JWarmup特性,前面提到,Java方法要執行2000次,然後在15,000次才達到一個非常高的效率,這些執行其實是非常慢的,要比慢50倍,並且解釋執行的時候,其他編譯器其實也是比較少有資源的,就邊編解釋執行還要邊編譯這個方法。
執行的時候CPU會打得非常高,響應特別慢,JWarmu就是說讓JVM提前知道哪些方法熱的,在處理請求之前就讓這些方法提前被編譯掉,從而避免了前面邊解釋,邊編譯的開銷。
模型如圖所示,首先應用被在被他環境被跑的時候,class被加載的時候,會產生一些日誌,告訴哪些class是熱的,然後這裡進行一個record,記了一個class list,然後把 class list就是真正熱的方法的一個logo文件,給分發到線上環境,然後線上環境的機器就知道哪些方法熱的,在真正處理用戶請求之前,會根據列表去提前把方法給編譯好,JWarmup它可以大大減少應用預熱的一個開銷。