http://www.bubuko.com/infodetail-2242063.html
1996 jdk1.0 (classic vm 虛擬機)
1997 jdk1.1
1998 jdk1.2 (javaee,javase區分)
2000 jdk1.3 (hotspot虛擬機發布)
2002 jdk1.4 (解密類庫,正則表達式)
2004 jdk1.5 (泛型,注解,裝箱,枚舉)
2004 jdk1.6 (jdbc4.0,腳本語言支持)
2011 jdk1.7 (新的垃圾回收器G1)
2014 jdk1.8 (Lambda表達式)
2016 jdk1.9 (模塊化)
oracle 兩個虛擬機 JRockit和HotSpot。jdk8要進行整合成一個。
只要符合jvm語言規范的標準,什么語言都可以在jvm中運行。
jvm保存數據使用的是2進制,2進制是8位,第一位是符合位,1代表負數。
jvm為什么要使用補碼? (正負數的補碼相同)
正數的原碼,反碼,補碼均相同。
負數的反碼在原碼的基礎上,最高位(符號位)不變,其他位0變1,1變0,補碼再在反碼的基礎上加1
0
正數:0000 0000
負數:1000 0000
反碼:1111 1111
補碼:0000 0000
-6+5 的運算?
-6的原碼:1000 0110
-6的反碼:1111 1001 (+1獲得補碼)
-6的補碼:1111 1010
5的原碼:0000 0101
5的補碼:0000 0101
1111 1010
- 0000 0101
= 1111 1111 (符號位參與運算)
-1
= 1111 1110(反碼)
= 1000 0001 (原碼)
jvm內存空間
堆 :1.對象實例 2.線程共享 3.GC主要工作區 4.數組 5.堆的劃分
方法區 :
虛擬機棧 :1.線程私有 2.棧是由棧幀組成,每個幀是一個方法(靜態和非靜態)。3.局部變量
Native棧 :
計數器 :
對象的棧上分配:1)小對象 2)直接分配在棧上,可以自動回收,減輕gc壓力
可見性:一個線程修改了變量,其他線程可以立刻知道
保證可見性的方法:
1)volatile :
2)synchronized
3)final : 被定義成常量的變量
線程的分類
1)守護線程:jvm自己使用的線程,例如:gc線程
2)普通線程:jvm中只要還存在普通線程,jvm就不會停止。
jvm加載并解析一個類后,將類信息保存到方法區,創建的對象保存對堆中。(方法區和堆是內存共享的)。
當一個線程被創建時,會被分配一個程序計數器(pc寄存器)和 虛擬機棧。虛擬機棧保存了該線程調用方法狀態,
一個方法一個棧幀,里面保存了方法的局部變量,方法參數,返回值。當方法執行時,該幀進入棧,當執行完成,
幀出棧。計數器保存該線程執行的行號。
堆:
當創建一個類的實例或者數組時,都在堆中為新對象分配內存。jvm中只要一個堆,所有的線程都共享他。
jvm的生命周期:
執行java程序,程序開始執行jvm開始運行,當程序結束時它就停止。
同一臺機子上運行三個程序,就會運行三個jvm。java的線程分兩種,
守護線程和普通線程。只有當jvm中沒有普通線程執行時,jvm就會停止。
守護線程是jvm自己使用的線程,比如GC線程。
1個字節 占8位
int 4個字節 32位
15.為什么要使用16進制?
人寫的是10進制,轉換為2進制的話,是32位太長了。
使用16進制就短了很多
16.class文件結構
魔數(4字節)+ class文件版本號(4個字節)+常量池 + 訪問標志
- 索引(知道父類是誰)+ 字段表集合(全局和類變量)+ 方法表集合
- 屬性表集合
常量池 = 字面量(字符串和常量) + 符號引用(類信息+方法描述)
17.操作數棧和寄存器的架構區別?
18.類加載時期:
加載 驗證 準備 解析 初始化 使用 卸載
19.類進行初始化的時機?
主動引用(會進行初始化)
1)使用new關鍵字實例化對象
2)讀取一個類的靜態字段
3)調用一個類的靜態方法
4)使用反射調用類
5)子類初始化時,發現父類未初始化,父類進行初始化
6)包含main()方法的主類
被動引用(不會進行初始化)
1)通過子類引用父類的靜態字段,不會導致子類初始化
2)通過數組定義來引用類,不會觸發此類的初始化 SupperClass[] sc = new SupperClass[10];
3) 使用(public static final String XXX)修飾的常量,不會初始化該類。
20.類的加載的過程,jvm需要做的事?
1)通過類全名獲取該類的2進制流
2)將字節流轉換為方法區可識別的數據結構
3)在堆中創建該對象,作為方法區這個類的訪問入口。
21.類的驗證過程?
主要目的就保證class文件符合jvm規范標準
1)文件格式驗證(魔數,主次版本號,常量池)
2)元數據驗證
- 該類是否有父類
- 父類是否繼承了不允許繼承的類
- 該類不是抽象類,父類是抽象類,是否都實現了
- 父類和子類之間的方法命名是否有沖突
3)字節碼驗證(保證程序語言 語義合法符合邏輯)
4)符號引用驗證 - 類全名是否能找到該類
- 通過字段描述是否找到方法和字段
- 引用類的訪問性能否被當前類訪問
22.類的準備過程?
為類變量(static修飾的)分配內存并設初始值(數據類型的初始值0)。
這個階段內存分配僅包括類變量,不包括實例變量,實例變量將會在對象
實例化時,隨著對象一起分配到java堆中。
23.類的初始化?
開始執行類中程序代碼
24.類加載器
- 啟動類加載器:將類庫加載到虛擬機中
- 擴展類加載器:負責加載第三方類庫
- 應用程序類加載器:
25.棧幀結構:
- 棧幀是用于支持虛擬機進行方法調用和執行的數據結構,其中保存了
局部變量表,操作數棧,動態鏈接,方法返回地址。 - 局部變量表:方法參數+局部變量
- 操作數棧:方法執行時,各種字節碼指令會出入
- 動態鏈接:鏈接運行時常量池引用(具體也沒明白)
- 方法返回地址:
通過javap指令來獲取類的字節碼
可以眼看反編譯class文件到java文件
mat來檢測內存溢出,lint 檢測代碼規范
內存溢出:
- 堆溢出 (原因:對象太多 解決辦法:增大堆空間)
- 永久區溢出 (原因:類的數量太多 解決辦法:增大perm區;允許class回收)
- jvm棧溢出 (原因:創建線程的需要分配的棧空間 > 操作系統分配的空間
解決辦法:減少堆空間;減少線程棧的大小) - 直接內存溢出 (原因:操作系統分配空間 < 堆空間+線程棧空間+直接內存
解決辦法:觸發GC)
內存管理,內存優化,oom
圖片緩存的兩種方式:軟引用,Lru算法
Lint進行代碼檢測 mat進行內存溢出檢測
內存優化
- 數據結構優化
- 頻繁使用數據添加使用StringBuilder
- ArrayMap,SparseArray 代替HashMap
- 內存抖動(突然申請大量內存,然后又取消)
- 對象復用
- 復用系統自帶資源
- ListView 的 ConvertView復用
- 避免在onDraw方法中執行對象創建
- 避免內存泄漏
- 內存泄漏導致可用Heap越來越少,頻繁觸發GC
- 尤其Activity的泄漏(內部類引起)
- 使用Application context而不是Activtiy的
- oom問題優化
- 臨時Bitmap對象的及時回收
- 加載Bitmap:縮放比例,解碼格式,局部加載
- 軟弱引用的使用