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

前幾篇文章分別從硬件、操做系統、虛擬機介紹了他們與內存之間的關係,咱們理解了內存底層的邏輯在應用時就會有事半功倍的效果,這篇文章主要介紹幾個使用方面的知識點java

1.volatile

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

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的機制實現的,若是有興趣能夠看看源碼,這裏就不細說了。jvm

4.java引用

強引用

強引用就是咱們平時使用的Object a = new Object()。若是一個對象具備強引用,那垃圾回收器就不會回收這個new Object()性能

軟引用

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

弱引用

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

虛引用

虛引用的get方法會直接返回null,虛引用的做用主要是對象在被回收時能夠經過虛引用通知到程序,對象被回收了。操作系統

相關文章
相關標籤/搜索