通過(guò)Java視角簡(jiǎn)單談?wù)劸植啃栽?/h1>
更新時(shí)間:2021年07月13日 14:29:47 作者:幻_
程序的局部性原理是指程序在執(zhí)行時(shí)呈現(xiàn)出局部性規(guī)律,即在一段時(shí)間內(nèi),整個(gè)程序的執(zhí)行僅限于程序中的某一部分,這篇文章主要給大家介紹了關(guān)于通過(guò)Java視角簡(jiǎn)單談?wù)劸植啃栽淼南嚓P(guān)資料,需要的朋友可以參考下
局部性原理
程序在訪問(wèn)數(shù)據(jù)時(shí),都趨于聚集在一片連續(xù)的區(qū)域中,這被稱為局部性原理。
按時(shí)間和空間劃分為兩類(lèi):
- 時(shí)間局部性:如果一個(gè)數(shù)據(jù)正在被訪問(wèn),那么近期它很可能再次被訪問(wèn)。
- 空間局部性:如果某一個(gè)位置的數(shù)據(jù)被訪問(wèn),那么這個(gè)問(wèn)題附近的數(shù)據(jù)很可能被訪問(wèn)。
針對(duì)局部性原理,CPU和操作系統(tǒng)都有具體的實(shí)現(xiàn)。
本文主要總結(jié)梳理CPU和操作系統(tǒng)的局部性原理在Java后端中的影響與意義。
CPU空間局部性
如下圖是Java的內(nèi)存模型

我們知道CPU為提高從內(nèi)存中讀數(shù)據(jù)的性能,有L1、L2、L3三個(gè)級(jí)別的高速緩存。
CPU利用局部性原理,在從內(nèi)存讀取數(shù)據(jù)項(xiàng)到緩存時(shí),將該內(nèi)存附近的數(shù)據(jù)塊也一并讀取到緩存中,這一過(guò)程稱為預(yù)讀。
即讀取連續(xù)空間的內(nèi)存要比內(nèi)存隨機(jī)訪問(wèn)的性能要高,這一點(diǎn)用Java程序可以證明。
public static void main(String[] args) {
int[][] arr = new int[10000][10000];
int sum = 0;
long startTime = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
sum += arr[i][j];
}
}
System.out.println("數(shù)組順序訪問(wèn)耗時(shí):" + (System.currentTimeMillis() - startTime) + "ms");
sum = 0;
startTime = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
sum += arr[j][i];
}
}
System.out.println("數(shù)組非順序訪問(wèn)耗時(shí):" + (System.currentTimeMillis() - startTime) + "ms");
}
這是一段對(duì)二維數(shù)組循環(huán)讀取的代碼。
程序的上半部分是按數(shù)組的第二維開(kāi)始順序讀取,即二維數(shù)組逐行按內(nèi)存連續(xù)空間順序訪問(wèn)。
下半部分則是按數(shù)組的第一維按列讀取,不是順序訪問(wèn)。
分別經(jīng)過(guò)10000*10000次的數(shù)組訪問(wèn)后,其運(yùn)行結(jié)果如下:

由此可見(jiàn),對(duì)內(nèi)存的順序訪問(wèn)性能優(yōu)于隨機(jī)訪問(wèn)。
磁盤(pán)空間局部性
在Java日常開(kāi)發(fā)中,很多的中間件都需要跟磁盤(pán)文件打交道,這些磁盤(pán)數(shù)據(jù)的高性能訪問(wèn)也都依托于局部性原理,比如:
- MySql的日志文件
- MQ消息數(shù)據(jù)
我們知道MySql的數(shù)據(jù)最終都保存在磁盤(pán)中,為減少磁盤(pán)IO提高性能,InnoDB引擎底層依托BufferPoll+redo log機(jī)制來(lái)提高mySql讀寫(xiě)性能(具體可參考MySql原理總結(jié))。而針對(duì)redo log、undo log、binlog的讀寫(xiě)避免不了磁盤(pán)IO,那么這里就利用操作系統(tǒng)的PageCache機(jī)制,對(duì)磁盤(pán)數(shù)據(jù)順序讀寫(xiě),使得磁盤(pán)IO的性能近乎于內(nèi)存性能。
我們常說(shuō)kafka和rocketMQ是高性能的消息中間件,其中一部分高性能就依托于對(duì)磁盤(pán)文件的順序讀寫(xiě)。比如commit log的順序?qū)懭?,kafka中partition、rockerMQ中consumerQueue中消息的順序讀寫(xiě)。同樣的也是利用操作系統(tǒng)的PageCache機(jī)制。
PageCache
頁(yè)緩存(PageCache)是OS對(duì)文件的緩存,用于加速對(duì)文件的讀寫(xiě)。一般來(lái)說(shuō),程序?qū)ξ募M(jìn)行順序讀寫(xiě)的速度幾乎接近于內(nèi)存的讀寫(xiě)速度,主要原因就是由于OS使用PageCache機(jī)制對(duì)讀寫(xiě)訪問(wèn)操作進(jìn)行了性能優(yōu)化,將一部分的內(nèi)存用作PageCache。
對(duì)于數(shù)據(jù)的寫(xiě)入,OS會(huì)先寫(xiě)入至Cache內(nèi),隨后通過(guò)異步的方式由pdflush內(nèi)核線程將Cache內(nèi)的數(shù)據(jù)刷盤(pán)至物理磁盤(pán)上。
對(duì)于數(shù)據(jù)的讀取,如果一次讀取文件時(shí)出現(xiàn)未命中PageCache的情況,OS從物理磁盤(pán)上訪問(wèn)讀取文件的同時(shí),會(huì)順序?qū)ζ渌噜弶K的數(shù)據(jù)文件進(jìn)行預(yù)讀取。
而PageCache就是局部性原理的實(shí)現(xiàn)。
時(shí)間局部性
時(shí)間局部性可能在我們?nèi)粘I(yè)務(wù)開(kāi)發(fā)中體現(xiàn)得更明顯。
類(lèi)似LRU緩存都是其具體實(shí)現(xiàn)。
另外CPU的指令重排序也貼點(diǎn)邊,比如對(duì)一個(gè)數(shù)據(jù)的訪問(wèn)計(jì)算,優(yōu)先將于這數(shù)據(jù)有關(guān)的指令排在一起處理。
參考
- 知乎:如何理解操作系統(tǒng)中的局部性原理
- gitHub:RocketMQ設(shè)計(jì)文檔
總結(jié)
到此這篇通過(guò)Java視角簡(jiǎn)單談?wù)劸植啃栽淼奈恼戮徒榻B到這了,更多相關(guān)Java局部性原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
-
SpringBoot與Quartz集成實(shí)現(xiàn)分布式定時(shí)任務(wù)集群的代碼實(shí)例
今天小編就為大家分享一篇關(guān)于SpringBoot與Quartz集成實(shí)現(xiàn)分布式定時(shí)任務(wù)集群的代碼實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧 2019-03-03
-
Java Spring循環(huán)依賴原理與bean的生命周期圖文案例詳解
這篇文章主要介紹了Spring循環(huán)依賴原理與bean的生命周期圖文案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下 2021-09-09
-
springboot詳解實(shí)現(xiàn)車(chē)險(xiǎn)理賠信息管理系統(tǒng)代碼
本系統(tǒng)基于Springboot開(kāi)發(fā)實(shí)現(xiàn)了一個(gè)為用戶車(chē)險(xiǎn)進(jìn)行理賠信息管理的一個(gè)信息化管理系統(tǒng),核心的業(yè)務(wù)主要是用戶申請(qǐng)保險(xiǎn)理賠,管理員審核進(jìn)入理賠程序,事故調(diào)查員對(duì)事故進(jìn)行調(diào)查和現(xiàn)場(chǎng)勘察,這其中共涉及到三類(lèi)用戶,購(gòu)買(mǎi)保險(xiǎn)的客戶,事故調(diào)查員和系統(tǒng)管理員 2022-06-06
-
logback使用MDCFilter日志過(guò)濾源碼解讀
這篇文章主要介紹了logback使用MDCFilter日志過(guò)濾源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪 2023-11-11
-
深入解析Java的設(shè)計(jì)模式編程中建造者模式的運(yùn)用
這篇文章主要介紹了深入解析Java的設(shè)計(jì)模式編程中建造者模式的運(yùn)用,同時(shí)文中也介紹了建造者模式與工廠模式的區(qū)別,需要的朋友可以參考下 2016-02-02
-
Mybatis批量修改時(shí)出現(xiàn)報(bào)錯(cuò)問(wèn)題解決方案
這篇文章主要介紹了Mybatis批量修改時(shí)出現(xiàn)報(bào)錯(cuò)問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下 2020-11-11
-
IDEA中application.properties的圖標(biāo)顯示不正常的問(wèn)題及解決方法
這篇文章主要介紹了IDEA中application.properties的圖標(biāo)顯示不正常的問(wèn)題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下 2021-04-04
-
java書(shū)店系統(tǒng)畢業(yè)設(shè)計(jì) 用戶模塊(3)
這篇文章主要介紹了java書(shū)店系統(tǒng)畢業(yè)設(shè)計(jì),第三步系統(tǒng)總體設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下 2016-10-10
-
Intellij IDEA使用restclient測(cè)試的教程圖解
這篇文章主要介紹了Intellij IDEA使用restclient測(cè)試的教程圖解,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下 2021-01-01
最新評(píng)論
局部性原理
程序在訪問(wèn)數(shù)據(jù)時(shí),都趨于聚集在一片連續(xù)的區(qū)域中,這被稱為局部性原理。
按時(shí)間和空間劃分為兩類(lèi):
- 時(shí)間局部性:如果一個(gè)數(shù)據(jù)正在被訪問(wèn),那么近期它很可能再次被訪問(wèn)。
- 空間局部性:如果某一個(gè)位置的數(shù)據(jù)被訪問(wèn),那么這個(gè)問(wèn)題附近的數(shù)據(jù)很可能被訪問(wèn)。
針對(duì)局部性原理,CPU和操作系統(tǒng)都有具體的實(shí)現(xiàn)。
本文主要總結(jié)梳理CPU和操作系統(tǒng)的局部性原理在Java后端中的影響與意義。
CPU空間局部性
如下圖是Java的內(nèi)存模型

我們知道CPU為提高從內(nèi)存中讀數(shù)據(jù)的性能,有L1、L2、L3三個(gè)級(jí)別的高速緩存。
CPU利用局部性原理,在從內(nèi)存讀取數(shù)據(jù)項(xiàng)到緩存時(shí),將該內(nèi)存附近的數(shù)據(jù)塊也一并讀取到緩存中,這一過(guò)程稱為預(yù)讀。
即讀取連續(xù)空間的內(nèi)存要比內(nèi)存隨機(jī)訪問(wèn)的性能要高,這一點(diǎn)用Java程序可以證明。
public static void main(String[] args) {
int[][] arr = new int[10000][10000];
int sum = 0;
long startTime = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
sum += arr[i][j];
}
}
System.out.println("數(shù)組順序訪問(wèn)耗時(shí):" + (System.currentTimeMillis() - startTime) + "ms");
sum = 0;
startTime = System.currentTimeMillis();
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
sum += arr[j][i];
}
}
System.out.println("數(shù)組非順序訪問(wèn)耗時(shí):" + (System.currentTimeMillis() - startTime) + "ms");
}
這是一段對(duì)二維數(shù)組循環(huán)讀取的代碼。
程序的上半部分是按數(shù)組的第二維開(kāi)始順序讀取,即二維數(shù)組逐行按內(nèi)存連續(xù)空間順序訪問(wèn)。
下半部分則是按數(shù)組的第一維按列讀取,不是順序訪問(wèn)。
分別經(jīng)過(guò)10000*10000次的數(shù)組訪問(wèn)后,其運(yùn)行結(jié)果如下:

由此可見(jiàn),對(duì)內(nèi)存的順序訪問(wèn)性能優(yōu)于隨機(jī)訪問(wèn)。
磁盤(pán)空間局部性
在Java日常開(kāi)發(fā)中,很多的中間件都需要跟磁盤(pán)文件打交道,這些磁盤(pán)數(shù)據(jù)的高性能訪問(wèn)也都依托于局部性原理,比如:
- MySql的日志文件
- MQ消息數(shù)據(jù)
我們知道MySql的數(shù)據(jù)最終都保存在磁盤(pán)中,為減少磁盤(pán)IO提高性能,InnoDB引擎底層依托BufferPoll+redo log機(jī)制來(lái)提高mySql讀寫(xiě)性能(具體可參考MySql原理總結(jié))。而針對(duì)redo log、undo log、binlog的讀寫(xiě)避免不了磁盤(pán)IO,那么這里就利用操作系統(tǒng)的PageCache機(jī)制,對(duì)磁盤(pán)數(shù)據(jù)順序讀寫(xiě),使得磁盤(pán)IO的性能近乎于內(nèi)存性能。
我們常說(shuō)kafka和rocketMQ是高性能的消息中間件,其中一部分高性能就依托于對(duì)磁盤(pán)文件的順序讀寫(xiě)。比如commit log的順序?qū)懭?,kafka中partition、rockerMQ中consumerQueue中消息的順序讀寫(xiě)。同樣的也是利用操作系統(tǒng)的PageCache機(jī)制。
PageCache
頁(yè)緩存(PageCache)是OS對(duì)文件的緩存,用于加速對(duì)文件的讀寫(xiě)。一般來(lái)說(shuō),程序?qū)ξ募M(jìn)行順序讀寫(xiě)的速度幾乎接近于內(nèi)存的讀寫(xiě)速度,主要原因就是由于OS使用PageCache機(jī)制對(duì)讀寫(xiě)訪問(wèn)操作進(jìn)行了性能優(yōu)化,將一部分的內(nèi)存用作PageCache。
對(duì)于數(shù)據(jù)的寫(xiě)入,OS會(huì)先寫(xiě)入至Cache內(nèi),隨后通過(guò)異步的方式由pdflush內(nèi)核線程將Cache內(nèi)的數(shù)據(jù)刷盤(pán)至物理磁盤(pán)上。
對(duì)于數(shù)據(jù)的讀取,如果一次讀取文件時(shí)出現(xiàn)未命中PageCache的情況,OS從物理磁盤(pán)上訪問(wèn)讀取文件的同時(shí),會(huì)順序?qū)ζ渌噜弶K的數(shù)據(jù)文件進(jìn)行預(yù)讀取。
而PageCache就是局部性原理的實(shí)現(xiàn)。
時(shí)間局部性
時(shí)間局部性可能在我們?nèi)粘I(yè)務(wù)開(kāi)發(fā)中體現(xiàn)得更明顯。
類(lèi)似LRU緩存都是其具體實(shí)現(xiàn)。
另外CPU的指令重排序也貼點(diǎn)邊,比如對(duì)一個(gè)數(shù)據(jù)的訪問(wèn)計(jì)算,優(yōu)先將于這數(shù)據(jù)有關(guān)的指令排在一起處理。
參考
- 知乎:如何理解操作系統(tǒng)中的局部性原理
- gitHub:RocketMQ設(shè)計(jì)文檔
總結(jié)
到此這篇通過(guò)Java視角簡(jiǎn)單談?wù)劸植啃栽淼奈恼戮徒榻B到這了,更多相關(guān)Java局部性原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot與Quartz集成實(shí)現(xiàn)分布式定時(shí)任務(wù)集群的代碼實(shí)例
今天小編就為大家分享一篇關(guān)于SpringBoot與Quartz集成實(shí)現(xiàn)分布式定時(shí)任務(wù)集群的代碼實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
Java Spring循環(huán)依賴原理與bean的生命周期圖文案例詳解
這篇文章主要介紹了Spring循環(huán)依賴原理與bean的生命周期圖文案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
springboot詳解實(shí)現(xiàn)車(chē)險(xiǎn)理賠信息管理系統(tǒng)代碼
本系統(tǒng)基于Springboot開(kāi)發(fā)實(shí)現(xiàn)了一個(gè)為用戶車(chē)險(xiǎn)進(jìn)行理賠信息管理的一個(gè)信息化管理系統(tǒng),核心的業(yè)務(wù)主要是用戶申請(qǐng)保險(xiǎn)理賠,管理員審核進(jìn)入理賠程序,事故調(diào)查員對(duì)事故進(jìn)行調(diào)查和現(xiàn)場(chǎng)勘察,這其中共涉及到三類(lèi)用戶,購(gòu)買(mǎi)保險(xiǎn)的客戶,事故調(diào)查員和系統(tǒng)管理員2022-06-06
logback使用MDCFilter日志過(guò)濾源碼解讀
這篇文章主要介紹了logback使用MDCFilter日志過(guò)濾源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
深入解析Java的設(shè)計(jì)模式編程中建造者模式的運(yùn)用
這篇文章主要介紹了深入解析Java的設(shè)計(jì)模式編程中建造者模式的運(yùn)用,同時(shí)文中也介紹了建造者模式與工廠模式的區(qū)別,需要的朋友可以參考下2016-02-02
Mybatis批量修改時(shí)出現(xiàn)報(bào)錯(cuò)問(wèn)題解決方案
這篇文章主要介紹了Mybatis批量修改時(shí)出現(xiàn)報(bào)錯(cuò)問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
IDEA中application.properties的圖標(biāo)顯示不正常的問(wèn)題及解決方法
這篇文章主要介紹了IDEA中application.properties的圖標(biāo)顯示不正常的問(wèn)題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
java書(shū)店系統(tǒng)畢業(yè)設(shè)計(jì) 用戶模塊(3)
這篇文章主要介紹了java書(shū)店系統(tǒng)畢業(yè)設(shè)計(jì),第三步系統(tǒng)總體設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
Intellij IDEA使用restclient測(cè)試的教程圖解
這篇文章主要介紹了Intellij IDEA使用restclient測(cè)試的教程圖解,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01

