日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

x86上的Java最终字段没有操作?

發(fā)布時間:2023/12/3 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 x86上的Java最终字段没有操作? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

我一直很樂于深入研究多線程編程的細(xì)節(jié),盡管閱讀了多年的CPU內(nèi)存一致性模型,無等待和無鎖算法,Java內(nèi)存模型,實踐中的Java并發(fā)性等知識,但我始終很喜歡。等等-我仍然會創(chuàng)建多線程編程錯誤。 總是令人驚奇的謙卑經(jīng)歷,使我想起這個問題有多復(fù)雜。

如果您已經(jīng)閱讀過JMM,那么您可能會記得,他們加強(qiáng)的領(lǐng)域之一是保證構(gòu)造函數(shù)完成后最終字段的可見性。 例如,

public class ClassA {public final String b;public ClassA(String b) {this.b = b;} } ... ClassA x = new ClassA("hello");

JMM指出,每個線程(甚至構(gòu)成ClassA實例x的線程除外)都將
始終將 xb視為“ hello”,并且永遠(yuǎn)不會看到null值(參考字段的默認(rèn)值)。

真是太好了! 這意味著我們可以通過將字段標(biāo)記為final來創(chuàng)建不可變對象,并且任何構(gòu)造的實例都可以自動在線程之間共享,而無需進(jìn)行其他工作來保證內(nèi)存可見性。 ! 不利的一面是,如果未將ClassA.b標(biāo)記為final,那么您將沒有此類保證。 其他線程可能會觀察到xb ==空結(jié)果(如果未使用其他“安全發(fā)布”機(jī)制來獲得可見性)

當(dāng)他們創(chuàng)建新的JMM時,每個人最喜歡的JCP成員Doug Lea都創(chuàng)建了一個食譜,以幫助JVM開發(fā)人員實現(xiàn)新的內(nèi)存模型規(guī)則。 如果閱讀此內(nèi)容,那么您將看到“規(guī)則”狀態(tài),即JIT編譯器應(yīng)在構(gòu)造函數(shù)返回之前立即發(fā)出StoreStore內(nèi)存屏障。 該StoreStore障礙是一種“內(nèi)存圍欄”。 如果在匯編指令中發(fā)出該信息,則意味著在對柵欄進(jìn)行重新排序之前, 在柵欄之前出現(xiàn)的內(nèi)存寫之前,不能對內(nèi)存進(jìn)行任何寫(存儲)操作。 請注意,它沒有說明讀取內(nèi)容,它們可以沿任一方向“跳”柵欄。

那么這是什么意思? 如果您考慮在調(diào)用構(gòu)造函數(shù)時編譯器的功能,那么很好:

String x = new ClassA("hello");get's broken down in to pseudo-code steps of:1. pointer_to_A = allocate memory for ClassA (mark word, class object pointer, one reference field for String b) 2. pointer_to_A.whatever class meta data = ... 3. pointer_to_A.b = address of "hello" string 4. emit a StoreStore memory barrier per the JMM 5. x = pointer_to_A

步驟4的StoreStore屏障可確保任何寫入(例如類元數(shù)據(jù)和對字段b的寫入)都不會在步驟5中對x進(jìn)行重新排序。這可以確保x是否對任何其他線程可見-如果沒有StoreStore內(nèi)存屏障,那么可以重新排序步驟3和5 ,并且在寫入xb和另一個cpu之前可能會出現(xiàn)對x的主內(nèi)存寫入操作內(nèi)核可以觀察到pointer_to_A.b為0(空),這將違反JMM。

好消息! 但是,如果您看一下該菜譜,就會發(fā)現(xiàn)一些有趣的事情:(1)很多人正在許多處理器體系結(jié)構(gòu)上編寫JVM! (2)x86上的所有*存儲屏障都沒有操作,除了StoreLoad屏障! 這意味著在x86上,上面的此StoreStore內(nèi)存屏障為空操作,因此不會為此發(fā)出任何程序集。 它什么都不做! 這是因為x86的內(nèi)存模型是強(qiáng)大的 “總存儲排序”(TSO)。 X86確保觀察到所有內(nèi)存寫入,就像它們都是以相同順序進(jìn)行的一樣。 因此,由于TSO的緣故,寫入5永遠(yuǎn)不會出現(xiàn)在任何其他線程的3之前,并且不需要發(fā)出存儲屏障。 其他cpu體系結(jié)構(gòu)的內(nèi)存模型較弱,無法保證這種效果,因此需要StoreStore內(nèi)存圍墻。 請注意,較弱的內(nèi)存模型雖然可能更難編程或較不直觀,但通常要快得多,因為cpu可以對事物進(jìn)行重新排序以更有效地使用緩存寫入并減少緩存一致性工作。

顯然,您應(yīng)該繼續(xù)遵循JMM編寫正確的代碼。 但是,這也意味著(不幸或幸運(yùn)的是)如果您在x86上運(yùn)行,忘記此操作不會導(dǎo)致錯誤……就像我在工作中所做的那樣。

為了真正鉆研這個家并確保沒有食譜中可能沒有描述的其他副作用,我按此處所述運(yùn)行了x86程序集輸出程序,并捕獲了為ClassA調(diào)用構(gòu)造函數(shù)的輸出(最后一個在引用類型字段)和ClassB的構(gòu)造函數(shù),該類與ClassA相同,除了在類成員上沒有final關(guān)鍵字之外。 x86程序集的輸出是相同的 。 因此,從JIT角度來看,在x86(非鈦合金,非arm等)上,final關(guān)鍵字沒有任何影響。

如果您想知道匯編代碼的外觀,則如下所示。 請注意,沒有任何鎖定說明。 當(dāng)Oracle的7u25 JRE發(fā)出x86 StoreLoad內(nèi)存隔離柵時,它是通過發(fā)出鎖addl $ 0x0,(%rsp)來完成的 ,它僅向堆棧指針添加零(無操作, 但由于其被鎖定),因此具有完整的作用。柵欄(符合StoreLoad柵欄的條件)。 x86中有幾種導(dǎo)致完全隔離的效果的方法,這些方法在OpenJDK郵件列表中進(jìn)行了討論。 他們觀察到至少在nehelem intel上,鎖添加0是最緊湊/有效的空間。

0x00007f152c020c60: mov %eax,-0x14000(%rsp)0x00007f152c020c67: push %rbp0x00007f152c020c68: sub $0x20,%rsp ;*synchronization entry; - com.argodata.match.profiling.FinalConstructorMain::callA@-1 (line 60)0x00007f152c020c6c: mov %rdx,(%rsp)0x00007f152c020c70: mov %esi,%ebp0x00007f152c020c72: mov 0x60(%r15),%rax0x00007f152c020c76: mov %rax,%r100x00007f152c020c79: add $0x18,%r100x00007f152c020c7d: cmp 0x70(%r15),%r100x00007f152c020c81: jae 0x00007f152c020cd60x00007f152c020c83: mov %r10,0x60(%r15)0x00007f152c020c87: prefetchnta 0xc0(%r10)0x00007f152c020c8f: mov $0x8356f3d0,%r11d ; {oop('com/argodata/match/profiling/FinalConstructorMain$ClassA')}0x00007f152c020c95: mov 0xb0(%r11),%r100x00007f152c020c9c: mov %r10,(%rax)0x00007f152c020c9f: movl $0x8356f3d0,0x8(%rax) ; {oop('com/argodata/match/profiling/FinalConstructorMain$ClassA')}0x00007f152c020ca6: mov %r12d,0x14(%rax) ;*new ; - com.argodata.match.profiling.FinalConstructorMain::callA@0 (line 60)0x00007f152c020caa: mov %ebp,0xc(%rax) ;*putfield a; - com.argodata.match.profiling.FinalConstructorMain$ClassA::@6 (line 17); - com.argodata.match.profiling.FinalConstructorMain::callA@6 (line 60)0x00007f152c020cad: mov (%rsp),%r100x00007f152c020cb1: mov %r10d,0x10(%rax) ;*new ; - com.argodata.match.profiling.FinalConstructorMain::callA@0 (line 60)0x00007f152c020cb5: mov %rax,%r100x00007f152c020cb8: shr $0x9,%r100x00007f152c020cbc: mov $0x7f152b765000,%r110x00007f152c020cc6: mov %r12b,(%r11,%r10,1) ;*synchronization entry; - com.argodata.match.profiling.FinalConstructorMain::callA@-1 (line 60)0x00007f152c020cca: add $0x20,%rsp0x00007f152c020cce: pop %rbp0x00007f152c020ccf: test %eax,0x9fb932b(%rip) # 0x00007f1535fda000; {poll_return}0x00007f152c020cd5: retq 0x00007f152c020cd6: mov $0x8356f3d0,%rsi ; {oop('com/argodata/match/profiling/FinalConstructorMain$ClassA')}0x00007f152c020ce0: xchg %ax,%ax0x00007f152c020ce3: callq 0x00007f152bfc51e0 ; OopMap{[0]=Oop off=136};*new ; - com.argodata.match.profiling.FinalConstructorMain::callA@0 (line 60); {runtime_call}0x00007f152c020ce8: jmp 0x00007f152c020caa ;*new; - com.argodata.match.profiling.FinalConstructorMain::callA@0 (line 60)0x00007f152c020cea: mov %rax,%rsi0x00007f152c020ced: add $0x20,%rsp0x00007f152c020cf1: pop %rbp0x00007f152c020cf2: jmpq 0x00007f152bfc8920 ; {runtime_call}

參考: x86上的Java final字段是否沒有操作? 來自我們的JCG合作伙伴史蒂夫·阿什(Steve Ash),來自“多杯咖啡”博客。

翻譯自: https://www.javacodegeeks.com/2013/11/java-final-fields-on-x86-a-no-op.html

總結(jié)

以上是生活随笔為你收集整理的x86上的Java最终字段没有操作?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。