前言
作爲Java程序員,你有沒有被JVM傷害過?面試的時候是否碰到過對JVM的靈魂拷問?
一、JVM 內存區域劃分
1.程序計數器(線程私有)
程序計數器(Program Counter Register),也有稱作爲 PC 寄存器。保存的是程序當前執行的指令的地址(也可以說保存下一條指令的所在存儲單元的地址),當 CPU 需要執行指令時,需要從程序計數器中得到當前需要執行的指令所在存儲單元的地址,然後根據得到的地址獲取到指令,在得到指令之後,程序計數器便自動加 1 或者根據轉移指針得到下一條指令的地址,如此循環,直至執行完所有的指令。也就是說是用來指示執行哪條指令的。
由於在 JVM 中,多線程是通過線程輪流切換來獲得 CPU 執行時間的,因此,在任一具體時刻,一個 CPU 的內核只會執行一條線程中的指令,因此,爲了能夠使得每個線程都在線程切換後能夠恢復在切換之前的程序執行位置,每個線程都需要有自己獨立的程序計數器,並且不能互相被幹擾,否則就會影響到程序的正常執行次序。因此,可以這麼說,程序計數器是每個線程所私有的。
在 JVM 規範中規定,如果線程執行的是非 native 方法,則程序計數器中保存的是當前需要執行的指令的地址;如果線程執行的是 native 方法,則程序計數器中的值是 undefined。
由於程序計數器中存儲的數據所佔空間的大小不會隨程序的執行而發生改變,因此,對於程序計數器是不會發生內存溢出現象(OutOfMemory)的。
異常情況:
不存在
2.Java 棧(線程私有)
3.本地方法棧(線程私有)
4.堆(線程共享)
5.方法區(線程共享)
6.直接內存(線程共享)
二、JVM 執行子系統
1.Class 類文件結構
1.1 Java 跨平臺的基礎
各種不同平臺的虛擬機與所有平臺都統一使用的程序存儲格式——字節碼(ByteCode)是構成平臺無關性的基石,也是語言無關性的基礎。Java 虛擬機不和包括 Java 在內的任何語言綁定,它只與“Class 文件”這種特定的二進制文件格式所關聯,Class 文件中包含了 Java虛擬機指令集和符號表以及若干其他輔助信息。
1.2 Class 類的本質
任何一個 Class 文件都對應着唯一一個類或接口的定義信息,但反過來說,Class 文件實際上它並不一定以磁盤文件的形式存在。Class 文件是一組以 8 位字節爲基礎單位的二進制流。
1.3 Class 文件格式
各個數據項目嚴格按照順序緊湊地排列在 Class 文件之中,中間沒有添加任何分隔符,這使得整個 Class 文件中存儲的內容幾乎全部是程序運行的必要數據,沒有空隙存在。Class 文件格式採用一種類似於 C 語言結構體的僞結構來存儲數據,這種僞結構中只有兩種數據類型:無符號數和表。
無符號數屬於基本的數據類型,以 u1、u2、u4、u8 來分別代表 1 個字節、2 個字節、4 個字節和 8 個字節的無符號數,無符號數可以用來描述數字、索引引用、數量值或者按照 UTF-8編碼構成字符串值。
表是由多個無符號數或者其他表作爲數據項構成的複合數據類型,所有表都習慣性地以“_info”結尾。表用於描述有層次關係的複合結構的數據,整個 Class 文件本質上就是一張表。
2.字節碼指令
2.1 加載和存儲指令
2.2 運算或算術指令
2.3 類型轉換指令
2.4 創建類實例的指令
2.5 創建數組的指令
2.6 訪問字段指令
2.7 數組存取相關指令
2.8 檢查類實例類型的指令
2.9 操作數棧管理指令
2.10 控制轉移指令
2.11 方法調用指令
2.12 方法返回指令
2.13 異常處理指令
2.14 同步指令
3.類加載機制
4.類加載器
4.1 系統的類加載器
4.2 雙親委派模型
5.Tomcat 類加載機制
6.方法調用詳解
6.1 解析
6.2 靜態分派
6.3 動態分派
6.4 基於棧的字節碼解釋執行引擎
三.垃圾回收器和內存分配策略
1.Java 中是值傳遞還是引用傳遞?
2.引用類型
3.基本垃圾回收算法
3.1.1 引用計數(Reference Counting):
比較古老的回收算法。原理是此對象有一個引用,即增加一個計數,刪除一個引用則減少一個計數。垃圾回收時,只用收集計數爲 0 的對象。此算法最致命的是無法處理循環引用的問題。
3.1.2 可達性分析清理
標記-清除(Mark-Sweep):此算法執行分兩階段。第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,把未標記的對象清除。此算法需要暫停整個應用,同時,會產生內存碎片。
複製(Copying): 此算法把內存空間劃爲兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另外一個區域中。次算法每次只處理正在使用中的對象,因此複製成本比較小,同時複製過去以後還能進行相應的內存整理,不會出現“碎片”問題。當然,此算法的缺點也是很明顯的,就是需要兩倍內存空間。
標記-整理(Mark-Compact):此算法結合了“標記-清除”和“複製”兩個算法的優點。也是分兩階段,第一階段從根節點開始標記所有被引用對象,第二階段遍歷整個堆,清除標記對象,並未標記對象並且把存活對象“壓縮”到堆的其中一塊,按順序排放。此算法避免了“標記-清除”的碎片問題,同時也避免了“複製”算法的空間問題。
3.1 按照基本回收策略分
3.2 按分區對待的方式分
3.3 按系統線程分
4.分代處理垃圾
5.JAVA 中垃圾回收 GC 的類型
四、編寫高效優雅 Java 程序
1.面向對象
1.1 構造器參數太多怎麼辦?
用 builder 模式,用在
(1)5 個或者 5 個以上的成員變量
(2)參數不多,但是在未來,參數會增加
Builder 模式:
屬於對象的創建模式,一般有
(1)抽象建造者:一般來說是個接口,包含
1)建造方法,建造部件的方法(不止一個)
2)返回產品的方法
(2) 具體建造者
(3) 導演者,調用具體的建造者,創建產品對象
(4)產品,需要建造的複雜對象
對於客戶端,創建導演者和具體建造者,並把具體建造者交給導演者,然後由客戶端通知導演者操縱建造者進行產品的創建。
在實際的應用過程中,有時會省略抽象建造者和導演者。
1.2 不需要實例化的類應該構造器私有
1.3 不要創建不必要的對象
1.4 避免使用終結方法
1.5 使類和成員的可訪問性最小化
1.6 使可變性最小化
1.7 複合優先於繼承
1.8 接口優於抽象類
2.方法
2.1 可變參數要謹慎使用
2.2 返回零長度的數組或集合,不要返回 null
2.3 優先使用標準的異常
3.通用程序設計
五、性能優化
一個 web 應用不是一個孤立的個體,它是一個系統的部分,系統中的每一部分都會影響整
個系統的性能
1.常用的性能評價/測試指標
1.1 響應時間
提交請求和返回該請求的響應之間使用的時間,一般比較關注平均響應時間。
常用操作的響應時間列表:
1.2 併發數
同一時刻,對服務器有實際交互的請求數。
和網站在線用戶數的關聯:1000 個同時在線用戶數,可以估計併發數在 5%到 15%之間,也就是同時併發數在 50~150 之間。
1.3 吞吐量
對單位時間內完成的工作量(請求)的量度
1.4 關係
系統吞吐量和系統併發數以及響應時間的關係:
理解爲高速公路的通行狀況:
吞吐量是每天通過收費站的車輛數目(可以換算成收費站收取的高速費),併發數是高速公路上的正在行駛的車輛數目,響應時間是車速。車輛很少時,車速很快。但是收到的高速費也相應較少;
隨着高速公路上車輛數目的增多,車速略受影響,但是收到的高速費增加很快;
隨着車輛的繼續增加,車速變得越來越慢,高速公路越來越堵,收費不增反降;
如果車流量繼續增加,超過某個極限後,任務偶然因素都會導致高速全部癱瘓,車走不動,當然後也收不着,而高速公路成了停車場(資源耗盡)。
2.常用的性能優化手段
2.1 避免過早優化
2.2 進行系統性能測試
2.3 尋找系統瓶頸,分而治之,逐步優化
2.4 前端優化常用手段
3 應用服務性能優化
3.1 緩存
3.1.1 緩存的基本原理和本質
3.1.2 合理使用緩衝的準則
3.1.3 分佈式緩存與一致性哈希
3.2 異步
3.2.1 同步和異步,阻塞和非阻塞
3.2.2 常見異步的手段
3.3 集羣
3.4 應用相關
3.4.1 代碼級別
3.4.2 併發編程
3.4.3 資源的複用
3.4.4 JVM
3.4.5 GC 調優
3.4.6 調優實戰
3.4.7 存儲性能優化
上面的這些問題只是給大家一個借鑑作用,最主要的是給自己增加知識的儲備,有備無患。
關於JVM與性能調優總結了將近50頁pdf文檔,歡迎關注我的公種浩:程序員追風,獲取這些整理的資料!
希望能幫助到你面試前的複習且找到一個好的工作,也節省大家在網上搜索資料的時間來學習。
最後
歡迎大家一起交流,喜歡文章記得關注我點個贊喲,感謝支持!