java程序員必須知道的內存知識-應用層

1.volatile

可見性,使用volatile修飾的變量可以立刻被其它線程讀取到,經常會被用到多線程同步的關鍵變量上,像aqs的state。
image.png
因爲CPU在訪問主存需要大約十幾個時鐘週期,爲了提高cpu的效率便有了高速緩存,當數據被加載到高速緩存時,其它核並不能第一時間看到。
內存屏障,最常見的就是雙檢鎖了,我們簡單的new對象在虛擬機內部其實需要很多操作,虛擬機爲了提高性能,會對我們代碼進行重排,使用volatile可以保證變量在被編譯時的順序性。
image.png
volatile、synchronized、final都會影響虛擬機的指令重排,會通過指令集中的loadload、storestore、loadstore、storeload四個內存屏障實現。
總結來說,volatile的作用有3個,編譯重排(虛擬機優化重排)、指令重排(cpu指令重排)、內存重排(高速緩存髒讀)

2.緩存行

緩存行是爲了解決cpu訪問主存時間長的問題,之前文章有過介紹。
https://segmentfault.com/a/11...
https://segmentfault.com/a/11...

緩存行友好

因爲緩存行會存儲多份數據,所以有了緩存行一致性協議,但一致性協議有一定成本,如果緩存行被共享,又頻繁修改,會導致性能下降。
解決辦法也很簡單,就是讓一個緩存行只有一條數據,保證數據獨享。
java8提供了jdk.internal.vm.annotation.Contended註解,在類上加註解後,虛擬機會自動做緩存行填充。

緩存行抖動

因爲緩存行大小有限,所以緩存行只能緩存部分數據,因爲緩存行採用映射的方式選擇緩存的數據,如下圖,如果ABCD四個數據都映射到上面的一個行裏,當我們要訪問A時,要把A加載進來,這時我們要訪問B,要把緩存行清空,再加載B,這時如果還需要訪問A,又得把B清掉,加載A,這就是緩存行抖動。
image.png
這是一個常見的例子,數組x和數組y同時映射到一個緩存行,當訪問x[i]時,要加載x到緩存行,訪問y[i]時又需要把y加載到緩存行,常見的解決辦法也比較簡單,就是擴充數組大小,讓x和y無法映射到一個緩存行。
image.png

3.內存池

堆內存由jvm替我們申請和回收,但是垃圾回收也是我們系統的瓶頸之一,所以有些時候爲了提高性能或者其他原因,我們也會使用堆外內存,例如netty,netty的堆外內存池使用的就是類似slab的機制實現的,如果有興趣可以看看源碼,這裏就不細說了。

4.java引用

強引用

強引用就是我們平時使用的Object a = new Object()。如果一個對象具有強引用,那垃圾回收器就不會回收這個new Object()

軟引用

如果一個對象只有軟引用,在內存充足時垃圾回收器就不會回收它;如果內存空間不足了,就會回收軟引用應用的對象。
軟引用的回收會根據上次gc剩餘內存,軟引用上次訪問的時間動態調整,就是上次訪問的時間越久,上次gc剩餘內存越少,越容易被回收。

弱引用

如果一個對象只有弱引用,只要觸發gc就會被回收(包括年輕代gc)。

虛引用

虛引用的get方法會直接返回null,虛引用的作用主要是對象在被回收時可以通過虛引用通知到程序,對象被回收了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章