大家在對比CPU參數的時候,一般都明白核顯線程、主頻等參數的高低影響,但是對于一些小的參數,例如三級緩存等并不很了解,甚至不懂三級緩存對于CPU來說到底有什么用,今天小牛就來和大家說一說三級緩存對于CPU有什么意義。
CPU緩存是CPU和內存之間的臨時存儲器,英文全名叫做Cache Memory。在CPU中,CPU緩存有三類,一級緩存,二級緩存和三級緩存。容量非常的小,常見的CPU的一級緩存都不到1MB。常見的CPU的三級緩存只有10MB左右。但是他們讀寫速度比內存快得多得多。這就是為什么會有CPU緩存這一事物的原因,內存速度太慢會拖垮CPU的處理速度,為了緩解這種延后,人們就設計出了CPU緩存來緩解這一情況。
緩存大小是非常重要的CPU參數,而且緩存的大小對CPU性能的影響非常大,CPU內緩存的運行速度極高,一般是和處理器同頻運作,工作效率遠遠大于系統內存和硬盤。實際工作時,CPU往往需要不斷重復讀取同樣的數據,而緩存容量的增大,可以大幅度提升CPU內部讀取數據的命中率,而不用再到速度更慢的內存或者硬盤上尋找,以此提高系統響應性能。但是從CPU芯片面積和成本的因素來考慮,緩存都很小。
CPU需要數據的時候,會先在一級緩存中尋找數據,一般一級緩存的數據命中率可以達到80%。如果一級緩存中找不到數據,CPU就會到二級緩存中尋找數據,如果依舊找不到的話,就會到三級緩存中找。有的CPU有四級緩存,三級緩存中沒有,那就到四級緩存中找。如果還是沒有的話,那就到大了幾千倍的內存中找。
目前的CPU,一級緩存和三級緩存的重要性最大,二級緩存已經基本被忽視了,廠家也很少會告知你該款CPU的一級緩存和二級緩存的容量大小,因為基本一致,大家都很小,所以就在上架緩存在比了起來。但是機友們也不必太過看重三級緩存,因為最影響CPU性能的因素不是緩存,而是核心和主頻高低。如果在一切因素都接近相同的情況下,三級緩存才能發揮出它的優勢。
本文原創不易,如果您喜歡這篇文章,想了解更多的電腦知識,歡迎點贊收藏加關注,有問題的小伙伴也可以撩我,謝謝大家的支持,我會繼續努力分享更多優質的內容!我是小牛,下期再見!
一級緩存里存的是成品對象,實例化和初始化都完成了,我們的應用中使用的對象就是一級緩存中的
二級緩存中存的是半成品,用來解決對象創建過程中的循環依賴問題
三級緩存中存的是 ObjectFactory<?> 類型的 lambda 表達式,用于處理存在 AOP 時的循環依賴問題
三級緩存的順序是由查詢循序而來,與在類中的定義順序無關
所以第一級緩存:singletonObjects ,第二級緩存:earlySingletonObjects ,第三級緩存:singletonFactories
接口注入的方式太靈活,易用性比較差,所以并未廣泛應用起來,大家知道有這么一說就好,不要去細扣了
構造方法注入的方式,將實例化與初始化并在一起完成,能夠快速創建一個可直接使用的對象,但它沒法處理循環依賴的問題,了解就好
setter 方法注入的方式,是在對象實例化完成之后,再通過反射調用對象的 setter 方法完成屬性的賦值,能夠處理循環依賴的問題,是后文的基石,必須要熟悉
代碼非常簡單: spring-no-dependence
Map<String, Object> singletonObjects 無 simpleBean
Map<String, Object> earlySingletonObjects 無 simpleBean
Map<String, ObjectFactory<?>> singletonFactories 無 simpleBean
Set<String> singletonsCurrentlyInCreation 有 simpleBean
Map<String, Object> singletonObjects 存儲的 simpleBean 的代理對象
Map<String, Object> earlySingletonObjects 由始至終都沒有 simpleBean 對象
Map<String, ObjectFactory<?>> singletonFactories 存入了一會數據立馬就刪除了 并未使用過
// 以下說的方法均在 AbstractAutowireCapableBeanFactory 類下
// createBeanInstance 通過反射完成對象的實例化,獲得半成品對象。 給分配內存空間, 即使半成品
// populateBean 填充半成品屬性, 如果有依賴對象則在這里引入
// initializeBean 初始化半成品對象
// applyBeanPostProcessorsAfterInitialization BeanPostProcessor的后置處理,AOP 的代理對象替換就是在這里完成的
代碼依舊非常簡單: spring-circle-simple 此時循環依賴的兩個類是: Circle 和 Loop
對象的創建過程與前面的基本一致,只是多了循環依賴,少了 AOP,所以我們重點關注: populateBean 和 initializeBean 方法
先創建的是 Circle 對象,那么我們就從創建它的 populateBean 開始,再開始之前,我們先看看三級緩存中的數據情況
Map<String, Object> singletonObjects 無 circle 也無 loop 對象
Map<String, Object> earlySingletonObjects 無 circle 也無 loop 對象
Map<String, ObjectFactory<?>> singletonFactories 只有 cicle 的 lambda
Set<String> singletonsCurrentlyInCreation 只有 circle
對 circle 對象的屬性 loop 進行填充的時候,去 Spring 容器中找 loop 對象,發現沒有則進行創建,又來到了熟悉的 createBean
此時三級緩存中的數據沒有變化,但是 Set<String> singletonsCurrentlyInCreation 中多了個 loop 標識loop正在創建中
loop 實例化完成之后,對其屬性 circle 進行填充,去 Spring 中獲取 circle 對象,又來到了熟悉的 doGetBean
此時一、二級緩存 (singletonObjects``earlySingletonObjects) 中都沒有 circle、loop ,而三級緩存中有這兩個
通過 getSingleton 獲取circle時,三級緩存調用了 getEarlyBeanReference ,但由于沒有 AOP,所以 getEarlyBeanReference 直接返回了普通的 半成品 circle
然后將 半成品 circle 放到了二級緩存,并將其返回,然后填充到了 loop 對象中
此時的 loop 對象就是一個成品對象了;接著將 loop 對象返回,填充到 circle 對象中,如下如所示
我們發現直接將 成品 loop 放到了一級緩存中,二級緩存自始至終都沒有過 loop ,三級緩存雖說存了 loop ,但沒用到就直接 remove 了
此時緩存中的數據,相信大家都能想到了
Map<String, Object> singletonObjects 無 circle 有 loop 對象
Map<String, Object> earlySingletonObjects 有 circle 無 loop 對象
Map<String, ObjectFactory<?>> singletonFactories 無 circle 也無 loop
Set<String> singletonsCurrentlyInCreation 有 circle 無 loop 因為loop創建完畢了
- `AbstractBeanFactory#doGetBean` 獲取bean
- -> `AbstractAutowireCapableBeanFactory#createBean` 創建bean
- -> `AbstractAutowireCapableBeanFactory#doCreateBean` 開始創建bean
- -> `AbstractAutowireCapableBeanFactory#addSingletonFactory` 把bean的一個 lambda 到三級緩存去了 singletonFactories
- -> `AbstractAutowireCapableBeanFactory#populateBean` 填充bean
- -> `AbstractAutowireCapableBeanFactory#applyPropertyValues` 檢查到有要添加的一來 進行填充
- -> `BeanDefinitionValueResolver#resolveValueIfNecessary` 注意 ! 這個位置獲取 loop 對象
```java
- 斷點 我們觀察下 三個緩存 Map的存儲情況
```java
Map<String, Object> singletonObjects 無 circle 也無 loop 對象
Map<String, Object> earlySingletonObjects 無 circle 也無 loop 對象
Map<String, ObjectFactory<?>> singletonFactories 有 circle 也無 loop 對象
Set<String> singletonsCurrentlyInCreation 有 circle
- `AbstractBeanFactory#doGetBean` 獲取bean
- -> `AbstractAutowireCapableBeanFactory#createBean` 創建bean
- -> `AbstractAutowireCapableBeanFactory#doCreateBean` 開始創建bean
- -> `AbstractAutowireCapableBeanFactory#addSingletonFactory` 把bean的一個 lambda 到三級緩存去了 singletonFactories
- -> `AbstractAutowireCapableBeanFactory#populateBean` 填充bean
- -> `AbstractAutowireCapableBeanFactory#applyPropertyValues` 檢查到有要添加的一來 進行填充
- -> `BeanDefinitionValueResolver#resolveValueIfNecessary` 注意 ! 這個位置改了。獲取的是 circle 對象
Map<String, Object> singletonObjects 無 circle 也無 loop 對象
Map<String, Object> earlySingletonObjects 無 circle 也無 loop 對象
Map<String, ObjectFactory<?>> singletonFactories 有 circle 有 loop 對象
Set<String> singletonsCurrentlyInCreation 有 circle 有 loop 說明兩個對象都在創建中
- `AbstractBeanFactory#doGetBean` 第二次獲取 circle
- `AbstractBeanFactory#getSingleton(beanName)` 獲取 Bean 的緩存
- `DefaultSingletonBeanRegistry#getSingleton(beanName, true)` 獲取 Bean 的緩存
- `DefaultSingletonBeanRegistry#isSingletonCurrentlyInCreation(beanName)` 關鍵!! 判斷 circle 這個名字的bean是不是在創建過程
- `this.singletonFactories.get(beanName)` 獲取這個 circle 的 lambda 創建函數
- `singletonFactory.getObject()` 調用函數 獲取了一個半成品的對象。 也就是 loop 還為空的 circle對象
- `this.earlySingletonObjects.put(beanName, singletonObject)` 將對象加入到二級緩存里面去 earlySingletonObjects 增加了對象
// 附,只有 earlySingletonObjects 新增了一個 circle 對象,其他map 無改變。 并且loop的 singletonFactories 也未使用到
代碼還是非常簡單:spring-circle-aop ,在循環依賴的基礎上加了 AOP
比上一種情況多了 AOP,我們來看看對象的創建過程有什么不一樣;同樣是先創建 Circle ,在創建Loop
創建過程與上一種情況大體一樣,只是有小部分區別,跟源碼的時候我會在這些區別上有所停頓,其他的會跳過,大家要仔細看
實例化 Circle ,然后填充 半成品 circle 的屬性 loop ,去 Spring 容器中獲取 loop 對象,發現沒有
則實例化 Loop ,接著填充 半成品 loop 的屬性 circle ,去 Spring 容器中獲取 circle 對象
這個過程與前一種情況是一致的,就直接跳過了,此時三級緩存中的數據如下:
Map<String, Object> singletonObjects 無 circle 也無 loop 對象
Map<String, Object> earlySingletonObjects 無 circle 也無 loop 對象
Map<String, ObjectFactory<?>> singletonFactories 有 circle 有 loop 對象
Set<String> singletonsCurrentlyInCreation 有 circle 有 loop 說明兩個對象都在創建中
我們發現從第三級緩存獲取 circle 的時候,調用了 getEarlyBeanReference 創建了 半成品circle的代理對象
將 半成品 circle 的代理對象放到了第二級緩存中,并將代理對象返回賦值給了 半成品 loop 的 circle 屬性
注意:此時是在進行 loop 的初始化,但卻把 半成品 circle 的代理對象提前創建出來了
loop 的初始化還未完成,我們接著往下看,又是一個重點,仔細看
在 initializeBean 方法中完成了 半成品 loop 的初始化,并在最后創建了 loop 成品 的代理對象
loop 代理對象創建完成之后會將其放入到第一級緩存中(移除第三級緩存中的 loop ,第二級緩存自始至終都沒有 loop )
然后將 loop 代理對象返回并賦值給 半成品 circle 的屬性 loop ,接著進行 半成品 circle 的 initializeBean
因為 circle 的代理對象已經生成過了(在第二級緩存中),所以不用再生成代理對象了;將第二級緩存中的 circle 代理對象移到第一級緩存中,并返回該代理對象
此時各級緩存中的數據情況如下(普通circle、 loop 對象在各自代理對象的 target 中)
Map<String, Object> singletonObjects 有 circle 代理對象 有 loop 代理對象
Map<String, Object> earlySingletonObjects 無 circle 無 loop 對象
Map<String, ObjectFactory<?>> singletonFactories 無 circle 無 loop 對象
Set<String> singletonsCurrentlyInCreation 無 circle 無 loop
我們回顧下這種情況下各級緩存的存在感,一級緩存仍是存在感十足,二級緩存有存在感,三級緩存挺有存在感
第三級緩存提前創建 circle 代理對象,不提前創建則只能給 loop 對象的屬性 circle 賦值成 半成品 circle ,那么 loop 對象中的 circle 對象就無 AOP 增強功能了
第二級緩存用于存放 circle 代理,用于解決循環依賴;也許在這個示例體現的不夠明顯,因為依賴比較簡單,依賴稍復雜一些,就能感受到了
第一級緩存存放的是對外暴露的對象,可能是代理對象,也可能是普通對象
所以此種情況下:三級緩存一個都不能少
- `AbstractBeanFactory#doGetBean` 第二次獲取 circle
- `AbstractBeanFactory#getSingleton(beanName)` 獲取 Bean 的緩存
- `DefaultSingletonBeanRegistry#getSingleton(beanName, true)` 獲取 Bean 的緩存
- `DefaultSingletonBeanRegistry#isSingletonCurrentlyInCreation(beanName)` 判斷 circle 這個名字的bean是不是在創建過程
- `this.singletonFactories.get(beanName)` 獲取這個 circle 的 lambda 創建函數
- `singletonFactory.getObject()` 調用函數 獲取了一個半成品的對象。(注意?。?有AOP環繞的對象在該位置會創建代理對象, 并且將代理對象 通過 AbstractAutoProxyCreator#getEarlyBeanReference 同步到AOP的創建類里邊。為了后面的使用) 也就是 loop 還為空的 circle對象
- `this.earlySingletonObjects.put(beanName, singletonObject)` 將對象加入到二級緩存里面去 earlySingletonObjects 增加了對象
// 附,只有 earlySingletonObjects 新增了一個 circle 對象,其他map 無改變。
- -> `AbstractAutowireCapableBeanFactory#populateBean` 填充完bean之后
- -> `AbstractAutowireCapableBeanFactory#initializeBean` 進行 circle 的初始化
- -> `AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization` bean的后置通知。此位置會進行 bean AOP的環繞 返回代理對象
- 由于在上方 loop 獲取 circle 的時候不是已經創建了個代理對象了嗎。那么這個aop就不能在新建一個代理類了。不然不一致
- 接著往下看
- -> `AbstractAutoProxyCreator#postProcessAfterInitialization` 創建代理對象
- -> `if (this.earlyProxyReferences.remove(cacheKey) !=bean)` 這個時候 二級緩存派上用場了。在這里。判斷是否已經有代理類了。如果有代理類則不新建代理類對象。
// 這樣 circle 的代理就不會被重復創建了。 二級緩存也派上了用場
沒有依賴,有AOP 這種情況中,我們知道 AOP 代理對象的生成是在成品對象創建完成之后創建的,這也是 Spring 的設計原則,代理對象盡量推遲創建
循環依賴 + AOP 這種情況中, circle 代理對象的生成提前了,因為必須要保證其 AOP 功能,但 loop 代理對象的生成還是遵循的 Spring 的原則
如果我們打破這個原則,將代理對象的創建邏輯提前,那是不是就可以不用三級緩存了,而只用兩級緩存了呢?
代碼依舊簡單:spring-circle-custom ,只是對 Spring 的源碼做了非常小的改動,改動如下
去除了第三級緩存,并將代理對象的創建邏輯提前,置于實例化之后,初始化之前;
1、三級緩存各自的作用
第一級緩存存的是對外暴露的對象,也就是我們應用需要用到的
第二級緩存的作用是為了處理循環依賴的對象創建問題,里面存的是半成品對象或半成品對象的代理對象
第三級緩存的作用處理存在 AOP + 循環依賴的對象創建問題,能將代理對象提前創建
2、Spring 為什么要引入第三級緩存
嚴格來講,第三級緩存并非缺它不可,因為可以提前創建代理對象
提前創建代理對象只是會節省那么一丟丟內存空間,并不會帶來性能上的提升,但是會破環 Spring 的設計原則
Spring 的設計原則是盡可能保證普通對象創建完成之后,再生成其 AOP 代理(盡可能延遲代理對象的生成)
所以 Spring 用了第三級緩存,既維持了設計原則,又處理了循環依賴;犧牲那么一丟丟內存空間是愿意接受的
原文鏈接:https://www.cnblogs.com/yunlongn/p/15638300.html
先我們要知道CPU緩存是什么,CPU緩存位于CPU與內存之間,起到臨時存儲器的作用。它的主要作用在于CPU的運行速度要遠高于內存速度,這會導致正常的運算過程中,CPU往往會等到內存將數據傳輸過來或者通過內存傳輸至其他硬件。CPU緩存的出現就是為了應對這類情況的出現,通常而言,CPU緩存容量比內存小但交換速度比內存快,當CPU調用大量數據時,就可先在CPU緩存中調用,從而加快讀取速度。
我們日常購買CPU的時候,會在參數表中看到有一級緩存、二級緩存、三級緩存指標,三種緩存的容量各不相同,他們之間的關系可以理解為每一級緩存中存儲的全部數據為下一級緩存的一部分,這三種緩存的技術難度和制造成本是相對遞減的,所以其容量也是相對遞增。
CPU緩存
一級緩存
一級緩存就在CPU的內核邊上,是與CPU連接最緊密的緩存,也是最早出現在CPU中緩解CPU與內存之間數據的緩存,
二級緩存
二級緩存是CPU的第二層高速緩存,L2高速緩存容量也會影響CPU的性能,原則是越大越好,現在家用CPU容量最大是4MB。
三級緩存
三級緩存是為讀取二級緩存后未命中的數據設計的一種緩存,在擁有三級緩存的CPU中,只有約5%的數據需要從內存中調用,這進一步提高了CPU的效率。
CPU緩存作用
作用之一就是我們之前提到的減少延遲,減少CPU與內存之間數據傳輸過程中的延遲時間。
作用之二則是提高命中率,CPU在Cache中找到有用的數據被稱為命中。未找到則訪問內存,對于用戶而言,當然更希望通過訪問CPU緩存中的信息已得到速度上的優勢。而CPU緩存的作用就是為了最大限度提升這一目標。
作用三是降低裝機成本。緩存的工作原理是當CPU要讀取一個數據時,首先從緩存中查找,同時把這個數據所在的數據塊調入緩存中,可以使得以后對整塊數據的讀取都從緩存中進行,不必再調用內存,進而降低裝機成本。
CPU緩存的作用其實就是提高命中率、降低延遲、降低內存開銷,其作用是為了提升CPU的工作效率。CPU緩存越大越好,尤其是一些專業設計、視頻渲染,由于CPU運算數據量大,對大緩存依賴較高。目前,隨著游戲畫質的越來越優化,對于CPU緩存的需求也越來越高。
本文編輯:劉國亮
關注泡泡網,暢享科技生活。