為什么要做這個周小結(jié)?因為每一周我都在接觸一些新的知識、技術(shù)、思想,一些東西看完一遍之后并沒有理解、吸收多少東西。所以抱著與其多看,不如精看的態(tài)度,在周末的時間,給自己泡一杯熱茶,舒適地坐在電腦前,簡單整理一下自己過去一周所了解到的、學(xué)到的各種東西。同時也希望能夠以這種方式、再加上對自己不停地提醒與督促去做,能夠培養(yǎng)自己這種經(jīng)常總結(jié)的好習(xí)慣吧。
本次小結(jié)的知識有:
- JAVA IO流
- 虛擬內(nèi)存
- 內(nèi)存分頁
1. JAVA IO流
JAVA IO流從數(shù)據(jù)編碼角度可以分為兩類:以字節(jié)為最小存儲單位的字節(jié)流和以相應(yīng)編碼組織起來的字符流
- 字節(jié)流
- InputStream
- OutputStream
InputStream體系結(jié)構(gòu)
上邊這幅圖是InputStream體系結(jié)構(gòu)的類關(guān)系圖。其中最上邊的 InputStream
為一個抽象類(Abstract Class)(注意不是接口 Interface).然后下邊是所有繼承自它的子類,其中在我日常工作開發(fā)中經(jīng)常用到的一般有 FileInputStream
, ObjectInputStream
, BufferedInputStream
, DataInputStream
這四個類。
首先來簡單分析一下 InputStream
這個抽象類(Abstract Class)。在我學(xué)習(xí)它的API的時候,重點看了下邊幾個:
abstract int read();
int read(byte[] b);
int read(byte[]b ,int off, int len);
可以看到該抽象類有一個抽象的read()方法,然后實現(xiàn)了后兩個方法。然后看完了這幾個方法的實現(xiàn)之后我發(fā)現(xiàn),原來后兩個方法其實在具體實現(xiàn)的時候,也是調(diào)用了第一個抽象方法read()
。
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
//調(diào)用了抽象方法read()
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
//調(diào)用了抽象方法read()
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
然后至于該抽象方法如何被實現(xiàn),抽象類 InputStream
不需要具體負(fù)責(zé),而是交給繼承自它的子類來搞定。(畢竟不同的子類,會對read()方法有不同的實現(xiàn)要求和方法)。
那么有關(guān)該read()方法是如何被子類實現(xiàn)的呢?
ByteArrayInputStream
類在構(gòu)造的時候會傳入一個字節(jié)數(shù)組的參數(shù),read()方法則是對該字節(jié)數(shù)組進行一個一個的讀取.
FileInputStream
類在構(gòu)造的時候需要傳入一個File文件對象或者文件的物理地址路徑,該read()方法不是上層JAVA代碼實現(xiàn)的,而是下層C++或者C實現(xiàn)的,因為該方法是一個native
方法。
DataInputStream
類經(jīng)常被用來修飾其他輸入流類。從它的構(gòu)造函數(shù)中就可以看出來。那么為什么該類會修飾其他類呢?有什么作用呢?眾所周知,該類可以讀取特定的數(shù)據(jù)類型,比如byte,boolean,char,int,float,double,.UTF等。該類read()方法調(diào)用的是參數(shù)傳遞進來的輸入流的read()方法,針對讀取不同的數(shù)據(jù)類型,就調(diào)用該read()方法讀取幾個字節(jié),然后拼接成一個特定的數(shù)據(jù)類型。感覺這個裝飾者當(dāng)?shù)拿惶搨餮剑?/p>
BufferedInputStream
類也是用來裝飾起來輸入流類,那么如何裝飾呢?設(shè)置一個緩沖池,其存在的價值就是一次性讀取一定數(shù)量的數(shù)據(jù),從而避免因為頻繁調(diào)用read()方法而造成一定的資源開銷。
以上是我在這一周的時間之內(nèi)對輸入流的一些簡單分析,不過很多東西說的還是比較籠統(tǒng),抽象。而且還有很多類沒有涉及到。
OutputStream體系結(jié)構(gòu)
剛剛把輸入流都簡單總結(jié)了一遍,那么對于輸出流來說,其在參數(shù)傳遞方面是差不多的,然后至于每一個輸出流最終要輸出到哪里,還是取決于我們調(diào)用的是哪一個輸出流類。
- 字符流
Reader體系結(jié)構(gòu)
Writer體系結(jié)構(gòu)
之所以叫做字符流,意思就是該流是在基于字節(jié)流的基礎(chǔ)上,再加上相應(yīng)的編碼方式,組織而成的字符流(char stream).
那么其實內(nèi)部的實現(xiàn)原理和字節(jié)流的差不多,不過有一個新的東西多了一個編碼。
2. 虛擬內(nèi)存
虛擬內(nèi)存是什么?
虛擬內(nèi)存這個稱呼,其實是和物理內(nèi)存相對應(yīng)的。它之所以叫做虛擬內(nèi)存,是因為這是由它的幾個
特點所決定的:
1.虛擬內(nèi)存是面向程序的,是為程序提供內(nèi)存地址服務(wù)的
2.虛擬內(nèi)存為程序分配的地址,是一塊"連續(xù)"的地址,這塊地址是連續(xù)的,這給程序造成了一種假象。
3.虛擬內(nèi)存的邏輯地址要大于實際的物理內(nèi)存地址
4.當(dāng)物理內(nèi)存地址不夠的時候,這個時候就要與磁盤來一個"置換"操作,把暫時用不到的程序臨時放到磁盤里,
然后把現(xiàn)在急需的程序給調(diào)入到內(nèi)存
3.內(nèi)存分頁
先介紹幾個專有名詞:
頁:線性地址(虛擬地址)被操作系統(tǒng)分成了以"頁"為基本單位的內(nèi)存。
頁框:物理內(nèi)存被分成了以"頁框"為基本單位的內(nèi)存。
頁表:負(fù)責(zé)維護頁與頁框映射關(guān)系的表項。
一級內(nèi)存分頁機制:
一個頁(頁框)的大小為4K,一個進程所占用的內(nèi)存大小為4GB,那么一共就需要1M個頁表項,同時一個頁表項的大小為4K,那么一個頁表所占用的內(nèi)存為4M。4M的內(nèi)存占用對于操作系統(tǒng)來說是一個很大的存儲開銷,所以引入了二級內(nèi)存分頁機制。
二級內(nèi)存分頁機制:
在第一級的基礎(chǔ)上,又引入了一個東西,叫做 頁目錄
。該頁目錄的大小為4K,占用了一個頁面的空間,其中有1k個表項,每個表項占用了4個字節(jié)。所謂表項
,指的就是一級內(nèi)存分頁機制中的頁表中的每個表項
。不過在二級機制中,頁表的大小不再是4M了,而是4K,其中有1K個表項。這樣通過再引入一個頁目錄
的方式實現(xiàn)了二級內(nèi)存分頁,而且能夠包含的頁表項同樣也能夠達到4M的大小。
4.性能優(yōu)化
關(guān)鍵點總結(jié):
- 代碼優(yōu)化
- 數(shù)據(jù)庫調(diào)優(yōu)(包括sql語句,連接池,NoSQL等)
- 架構(gòu)調(diào)優(yōu)(讀寫分離,負(fù)載均衡,水平和垂直分庫分表)
- 緩存(本地緩存 HashMap, Ehcache, 緩存服務(wù) Redis/Tair/Memcache)
- 異步(及時返回)
- JVM調(diào)優(yōu)
- 多線程與分布式
- 度量、監(jiān)控系統(tǒng) 提供間接幫助 來優(yōu)化系統(tǒng)
后記:這次的周小結(jié)包含自己在本周所看到過,覺著不錯的,同時又是自己比較薄弱的一些知識,目的不在于把某一個知識研究得有多深,更多的相當(dāng)于是自己的一個讀書筆記吧。通過再次總結(jié)一遍,加深自己對這些知識的印象。平時隨手寫上自己的感悟、小結(jié)之類的東西,把功夫用在平似乎,這樣每周也不用花太多的時間來重新梳理。
參考資料:
JAVA IO流
博客園:虛擬內(nèi)存
維基百科:虛擬內(nèi)存
他人博客:內(nèi)存分頁
內(nèi)存分頁
常見性能優(yōu)化策略的總結(jié)