摘要:為此,引入轉換查找緩沖緩存最近的轉換記錄。這個優化技術,可以看到將原本對對象的字段訪問,替換為一個局部變量的訪問。當所有線程都在已知的位置停止的時候,被認為是到達了安全點。檢查安全點請求的代碼
1、JVM鎖粗化和循環
原文標題:JVM Anatomy Quark #1: Lock Coarsening and Loops
眾所周知Hotsport編譯器會進行JVM鎖粗化和優化,它將相鄰的鎖區塊進行合并,有效減少鎖的的占用成本,類似
synchronized (obj) { // statements 1 } synchronized (obj) { // statements 2 }
優化成
synchronized (obj) { // statements 1 // statements 2 }
那么在循環體中是否也會進行相同的優化?類似
for (...) { synchronized (obj) { // something } }
優化成
synchronized (this) { for (...) { // something } }
實際上是不會的,理論上來說是可以的,這有點像針對鎖的循環無關代碼外提。然而如此優化的缺點是將鎖的粒度增加太多,線程在執行循環時將會長時間獨占鎖
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
2、透明大頁
原文標題:JVM Anatomy Quark #2: Transparent Huge Pages
進程都擁有自己的虛擬內存空間,虛擬內存空間會映射到實際內存。例如,兩個進程可以在相同的虛擬地址 0x42424242 中存儲不同數據,這些數據實際存放在不同的物理內存中。當程序訪問該地址時,通過某種機制會把虛擬地址轉換成實際物理地址
這個過程一般通過由操作系統維護的頁表實現,硬件通過"遍歷頁表"進行地址轉換。雖然以頁面為單位進行地址轉換更容易,但由于每次訪問內存都會發生地址轉換會帶來不小開銷。為此,引入TLB(轉換查找緩沖)緩存最近的轉換記錄。TLB要求至少要與 L1 緩存一樣快,因此通常緩存少于100條。對工作負載較大的情況,TLB缺失和由此引發的頁表遍歷需要很多時間
TLB容量比較小,但是我們可以將地址轉換的頁面容量增大,這個可以借助系統內核的透明大頁機制輕松做到,那這樣是否會對性能有所幫助呢?
實際上它能有效提高應用程序性能,特別是當程序擁有大量數據和堆棧時
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
3、GC設計和停頓
原文標題:JVM Anatomy Quark #3: GC Design and Pauses
常見GC算法如下所示,其中黃色為stop-the-world階段,綠色為并發階段
需要注意不同收集器在常規GC循環中何時會暫停
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
4、TLAB內存分配
原文標題:JVM Anatomy Quark #4: TLAB allocation
本小節將揭曉,什么是Bump-the-pointer技術跟蹤?什么是TLAB內存分配?
Bump-the-pointer技術跟蹤在eden區創建的最后一件對象,最后該對象會放在eden頂部,之后再創建對象時,只需要檢查最后一個對象就可以知道eden空間容量是否足夠,但是在多線程環境中就會出現問題,不過加鎖同步開銷太大,于是提出TLAB
TLAB(Thread-local allocation buffer)緩沖區,特點是每個線程獨享一份,也就意味著不存在數據共享也就不需要加鎖同步,同時它結合了Bump-the-pointer跟蹤技術實現快速的對象分配
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
5、TLAB與堆可解析性
原文標題:JVM Anatomy Quark #5: TLABs and Heap Parsability
好的垃圾回收器通常會保證堆的可解析性,意味著它不需要復雜的數據結構也能以某種方式解析成對象或者字段。雖然嚴格來說,它在分配周期中并不是始終以對象流的方式存在,但是它使得GC實現、測試、調戲變得輕易
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
6、創建對象階段
原文標題:JVM Anatomy Quark #6: New Object Stages
你可能聽說過分配并不是初始化。但是 Java 有構造方法!構造方法是分配?還是初始化?
Java語言中的new對應很多字節碼指令,比如
public Object t() { return new Object(); }
編譯為
public java.lang.Object t(); descriptor: ()Ljava/lang/Object; flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: new #4 // class java/lang/Object 3: dup 4: invokespecial #1 // Method java/lang/Object."":()V 7: areturn
給人感覺是,new關鍵會執行分配資源和系統初始化,同時調用構造方法執行用戶初始化,但是聰明的虛擬機會進行優化,比如在構造方法執行完成之前觀察對象使用情況然后選擇性合并任務
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
7、初始化開銷
原文標題:JVM Anatomy Quark #7: Initialization Costs
初始化對象或者數組是實例化過程中最主要的開銷,使用TLAB分配,對象或者數據初始化的開銷取決于元數據寫入和內容的初始化
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
8、局部變量可用性
原文標題:JVM Anatomy Quark #8: Local Variable Reachability
離開了當前作用域,存儲在局部變量中的引用才會被回收,這種說法正確嗎?在Java中并非如此,Java局部變量的可用性不由代碼塊決定,而與最后一次使用有關,并且可能會持續到最后一次使用為止。使用像finalizer、強引用、弱引用、虛引用這樣的方法通知對象不可達,會受到“提前檢查”優化帶來的影響,使得代碼塊還沒有結束變量可能已不可用,這是一種很好的特性,使得GC能提前回收掉本地分配的大量緩存
當然如果想獲得C++編程那種代碼塊結束時才釋放的特性,你可以使用try-finally
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
9、JNI 臨界區 與 GC 鎖
原文標題:JVM Anatomy Quark #9: JNI Critical and GC Locker
10、String中的intern方法
原文標題:JVM Anatomy Quark #10: String.intern()
我們知道intern方法會從字符串常量池中查詢當前字符串是否存在,若不存在就會將當前字符串放入常量池中,從而使得字符串對象被緩存了一樣
JAVA使用JNI調用c++實現的StringTable的intern方法, StringTable的intern方法跟Java中的HashMap的實現是差不多的, 只是不能自動擴容。默認大小是1009
要注意的是,String的String Pool是一個固定大小的Hashtable,默認值大小長度是1009,如果放進String Pool的String非常多,就會造成Hash沖突嚴重,從而導致鏈表會很長,而鏈表長了后直接造成的影響就是調用String.intern時性能會大幅下降
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
11、移動GC與局部性
原文標題:JVM Anatomy Quark #11: Moving GC and Locality
標記-壓縮回收器可以保持堆中對象的分配順序,也可以對其任意重排。雖然任意順序能夠比其他標記-壓縮回收器速度更快,也不會帶來空間開銷,但是會破壞應用線程的局部性
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
12、本地內存跟蹤
原文標題:JVM Anatomy Quark #12: Native Memory Tracking
JVM的默認配置通常是為長時間運行的服務器應用準備的,包括GC、內部數據結構的初始大小、堆棧大小等也是如此,而通過NMT探索虛擬機內存分配情況能讓我們立刻知道從哪里入手優化應用占用的內存,同時非常有助于在應用實際生產環境中調整JVM參數
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
13、屏障
原文標題:JVM Anatomy Quark #13: Intergenerational Barriers
GC通常會有屏障組,即使沒有實際發生回收,這些屏障也會影響應用程序的性能。即使串行、并行這樣非常基本的分代收集器,也至少有一個引用存儲屏障,而像G1這樣更高級的回收器會有更復雜的屏障跟蹤不同區域間的引用。某些情況下,這種開銷讓人非常痛苦
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
14、常量變量
原文標題:JVM Anatomy Quark #14: Constant Variables
停留2秒思考下面的代碼塊會輸出什么
import java.lang.reflect.Field; public class ConstantValues { final int fieldInit = 42; final int instanceInit; final int constructor; { instanceInit = 42; } public ConstantValues() { constructor = 42; } static void set(ConstantValues p, String field) throws Exception { Field f = ConstantValues.class.getDeclaredField(field); f.setAccessible(true); f.setInt(p, 9000); } public static void main(String... args) throws Exception { ConstantValues p = new ConstantValues(); set(p, "fieldInit"); set(p, "instanceInit"); set(p, "constructor"); System.out.println(p.fieldInit + " " + p.instanceInit + " " + p.constructor); } }
正常會打印出42 9000 9000,也就是說即使通過反射重寫了fieldInt字段的值,我們也無法觀察到最新的值,而更新另外兩個字段生效了,這個奇怪結果的解釋是方法內聯
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
15、即時常量
編譯器信任static final字段,因為這個值不依賴特定對象,而且是不能改變的
https://shipilev.net/jvm/anat...
16、超多態虛調用
https://shipilev.net/jvm/anat...
17、信任非靜態Final字段
原文標題:JVM Anatomy Quark #17: Trust Nonstatic Final Fields
class M { final int x; M(int x) { this.x = x; } } static final M KNOWN_M = new M(1337); void work() { // We know exactly the slot that holds the variable, can we just // inline the value 1337 here? return KNOWN_M.x; }
上面這段代碼是否會進行方法內聯優化呢?實際上是不會的,如果要信任實例final字段,那么必須知道當前操作的對象,然而上面那段代碼是引用關系
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
18、字面量替換
原文標題:JVM Anatomy Quark #18: Scalar Replacement
利用逃逸分析然后編譯器優化可以實現在棧上分配而不是堆上分配,方法退出后直接彈出釋放,無助借助垃圾回收器處理,很神奇,對嗎?
不過一旦發生了逃逸現象,我們需要將實體對象完整地復制到堆中。而且由于實現起來需要更改大量假設了"對象只能在堆上分配"的代碼,因為HotSpot虛擬機并沒有采用棧上分配,而是標量替換這么一項技術。這個優化技術,可以看到將原本對對象的字段訪問,替換為一個局部變量的訪問。該對象沒有被實際分配,因此和棧上分配一樣,它同樣可以減輕垃圾回收的壓力
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
19、鎖消除
原文標題:JVM Anatomy Quark #19: Lock Elision
目前的內存模型中,對不共享的對象進行加鎖操作是無效的,編譯器不會對它做任何事情。由于其他線程不能獲取該鎖對象,因此也無法基于該鎖對象構造兩個線程之間的happens-before規則。那么編譯器只需證明鎖對象不會發生逃逸,便可以進行鎖消除
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
20、FPU溢出
原文標題:JVM Anatomy Quark #20: FPU Spills
寄存器分配器的職責是,維護在特定的編譯單元中程序需要的所有操作數的程序表示,并且映射這些虛操作數到實際的機器寄存器,也就是為它們分配寄存器。在許多真實的程序中,在給定程序位置,虛操作數的數量會大于可用機器寄存器的數量,那么寄存器分配器就需要將某些操作數放到寄存器之外的其它位置比如放到棧上,這種就稱為FPU溢出,有效緩解了寄存器壓力
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
21、堆內存歸還
原文標題:JVM Anatomy Quark #21: Heap Uncommit
許多GC已經實現了在合適的時機歸還堆內存:Shenandoah異步執行堆內存歸還,即使沒有GC請求;G1在顯式GC請求中執行堆內存歸還;Serial和Parallel在某些條件下也會執行。不過歸還內存可能會耗費一些時間,所以實際的實現會在歸還內存之前會增加一個超時時間
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
22、安全點檢查
原文標題:JVM Anatomy Quark #22: Safepoint Polls
在大部分機器上停止運行的線程實際上是很簡單的:向線程發送一個信號,強制處理器中斷,停止線程正在執行的操作,將控制權轉交給別處。然而,這還不足以讓Java線程在任意位置停止,特別是如果你需要精確的垃圾回收。在這種情況下,你需要知道寄存器和棧中的內容,這些內容可能是你需要處理的對象引用?;蛘呷绻阆胍∠蜴i,你需要精確的知道線程的狀態和獲取的鎖
因此Hotspot實現了協作機制:線程經常詢問是否應該將控制權交給VM,在線程生命周期中某些已知的位置,線程的狀態是已知的。當所有線程都在已知的位置停止的時候,VM 被認為是到達了安全點。檢查安全點請求的代碼片段因此被稱為安全點檢查
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
23、壓縮引用
原文標題:JVM Anatomy Quark #23: Compressed References
大部分JVM實現將Java引用轉換為機器指針,沒有額外的迂回,這簡化了性能問題,不過通常情況下會使得引用的表示比機器指針的寬度小,也就是進行壓縮引用,比如你可以使用XX:+UseCompressedOops選項,使得在64位系統中對象指針可以使用32bit的Compressed版本。壓縮方法可以是比特右移,稱為“基于零的壓縮普通對象指針”,但是基于零的壓縮引用仍然依賴堆內存映射在較低地址的假設。如果不是,我們可以使用非零的堆內存起始地址來解碼
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
24、對象對齊
原文標題:JVM Anatomy Quark #24: Object Alignment
許多硬件實現要求對數據的訪問是對齊的,也就是N字節寬度數據的訪問地址總是N的倍數,否則會直接拒絕操作,產生SIGBUS信號或者其他硬件異常
在Hotspot中最小的對象對齊是8字節,我們可以通過-XX:ObjectAlignmentInBytes選項進行調整,不過會有正面和負面的后果
負面的后果是每個對象平均的內存空間浪費將會增加,如果啟用壓縮引用,這個增加會變得不那么明顯,不過內存對齊會導致壓縮引用閾值被移動,因為它依賴引用中有多少低位比特是零,這很有趣,總之,利器當慎用
翻譯修改摘錄自:
https://shipilev.net/jvm/anat...
文章來源:www.liangsonghua.me
作者介紹:京東資深工程師-梁松華,在穩定性保障、敏捷開發、JAVA高級、微服務架構方面有深入的理解
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76072.html
摘要:最黑科技的玩法就是字節碼編程,也就是動態修改或是動態生成字節碼。使用字節碼編程可以玩出很多高級玩法,其中最高級的玩法是在程序運行時進行字節碼修改和代碼注入。 showImg(https://segmentfault.com/img/bVbkqqg?w=1142&h=640);這個是我訂閱 陳皓老師在極客上的專欄《左耳聽風》,我貼出來是為了自己以后方便學習和記憶,同時也分享給你們一起學習...
摘要:理解進公園背景這個公園有一個總公園總公園里有許多小公園總公園是登錄頁面小公園是域名相同的頁面第一次進總公園第一次請求某個服務器工作人員檢查你的入園是否符合條件后端查看是否是注冊以后的用戶通過條件的話工作人員會給你一張票后端會給你一個響應頭這 Cookie, Session, LocalStorage, SessionStorage Cookie 理解 進公園 背景: 這個公園有一個總公...
摘要:自動選擇最好路線并支持自動重連擁有自動維護的連接池,減少握手次數,減少請求延遲,共享減少對服務器的請求次數。支持的緩存策略減少重復的網絡請求。擁有輕松處理請求與響應并自動處理數據壓縮。支持自簽名的鏈接,配置有效證書即可。 1.OkHttp簡介: Okhttp與HttpClient、HttpUrlConnection以及Volley類似是一個網絡請求框架,支持連接同一地址的鏈接共享同一個...
閱讀 725·2021-11-17 09:33
閱讀 3757·2021-09-01 10:46
閱讀 1751·2019-08-30 11:02
閱讀 3280·2019-08-29 15:05
閱讀 1396·2019-08-26 11:39
閱讀 2272·2019-08-23 17:04
閱讀 1973·2019-08-23 15:43
閱讀 1371·2019-08-23 14:12