[TOC]
# JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)
圖示一:

圖示二:

圖示三:

圖示四:

Java代碼被編譯器編譯成字節(jié)碼之后,JVM開(kāi)辟一片內(nèi)存空間(也叫運(yùn)行時(shí)數(shù)據(jù)區(qū)),通過(guò)類(lèi)加載器加到到運(yùn)行時(shí)數(shù)據(jù)區(qū)來(lái)存儲(chǔ)程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息,在這個(gè)數(shù)據(jù)區(qū)中,它由以下幾部分組成:
1、程序計(jì)數(shù)器:線程私有,記錄著當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令。
2、虛擬機(jī)棧:線程私有,棧中存放著棧幀,每個(gè)棧幀分別對(duì)應(yīng)一個(gè)被調(diào)用的方法,方法的調(diào)用過(guò)程對(duì)應(yīng)棧幀在虛擬機(jī)中入棧到出棧的過(guò)程。每個(gè)棧幀中存儲(chǔ)有局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等。
3、本地方法棧:線程私有,和虛擬機(jī)?;疽恢拢菫閳?zhí)行native方法而服務(wù)的。
4、堆:線程共有,是JVM所管理的內(nèi)存中最大的一塊,用來(lái)存儲(chǔ)對(duì)象實(shí)例和數(shù)組,在JVM中只有一個(gè)堆,但可以處于物理上不連續(xù)的內(nèi)存空間中。由于現(xiàn)在垃圾收集器通常采用分代回收算法,因此堆可以細(xì)分為永久世代(permanent)、成熟世代(tenured)、年輕世代(young)。
5、方法區(qū):線程共有,用來(lái)存放已經(jīng)加載的類(lèi)信息、常量、靜態(tài)變量以及即時(shí)編譯器編譯后的代碼等。
6、運(yùn)行時(shí)常量池:是方法區(qū)的一部分,用于存儲(chǔ)編譯期生成的各種字面量和符號(hào)引用。
有一點(diǎn)需要注意的是,當(dāng)局部變量重新賦值時(shí),基本數(shù)據(jù)類(lèi)型的數(shù)據(jù)本身是不會(huì)改變的。并不是在內(nèi)存中改變字面量?jī)?nèi)容,而是重新在棧中尋找已存在的相同的數(shù)據(jù),若棧中不存在,則重新開(kāi)辟內(nèi)存存新數(shù)據(jù),并且把要重新賦值的局部變量的引用指向新數(shù)據(jù)所在地址。
# GC垃圾回收
1、JVM中的棧記錄了線程的方法調(diào)用,每個(gè)線程擁有一個(gè)棧,在線程運(yùn)行的過(guò)程中,如果有新的方法調(diào)用,那么該線程對(duì)應(yīng)的棧就會(huì)增加一個(gè)棧幀,棧幀中存儲(chǔ)有該方法的參數(shù)、局部變量、返回地址。Java的參數(shù)和局部變量只能是基本數(shù)據(jù)類(lèi)型或者對(duì)象引用,因此棧幀中僅存儲(chǔ)基本數(shù)據(jù)類(lèi)型和引用,而對(duì)象引用所引用的對(duì)象存儲(chǔ)在堆中。方法調(diào)用結(jié)束時(shí),該棧幀會(huì)被刪除,參數(shù)和局部變量占用的空間也被刪除,但對(duì)象引用所引用的對(duì)象還在堆中。
2、Java對(duì)象都存儲(chǔ)在堆中,為線程共享的,在某個(gè)方法中創(chuàng)建的對(duì)象,可以在方法調(diào)用結(jié)束后,依舊存在于堆中。當(dāng)該對(duì)象不會(huì)再被引用時(shí),卻仍然占用著堆中的空間,就造成了內(nèi)存泄漏。
3、Java中的垃圾回收可以自動(dòng)清空堆中不再使用的對(duì)象,防止內(nèi)存泄漏,有效的使用空閑的內(nèi)存。
## 垃圾回收機(jī)制算法
垃圾回收算法需要做兩件事,一是發(fā)現(xiàn)無(wú)用信息對(duì)象,二是回收被無(wú)用對(duì)象占用的空間,使該空間可被程序再次使用。

### 引用計(jì)數(shù)算法
每個(gè)對(duì)象包含一個(gè)計(jì)數(shù)器,當(dāng)有新的指向該對(duì)象的引用時(shí),計(jì)數(shù)器加1,當(dāng)引用移除時(shí),計(jì)數(shù)器減1,當(dāng)計(jì)數(shù)器為0時(shí),認(rèn)為該對(duì)象可以回收。
引用計(jì)數(shù)法有一個(gè)缺陷,無(wú)法檢測(cè)出循環(huán)引用。
```java
public class Main {
public static void main(String[] args) {
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
object1.object = object2;
object2.object = object1;
object1 = null;
object2 = null;
}
}
```
上面例子在最后將對(duì)象引用object1和object2都置為null后,他們所引用的對(duì)象均不會(huì)再被使用,但是兩個(gè)對(duì)象相互引用對(duì)方,導(dǎo)致兩者的計(jì)數(shù)器都不為0,因此不能被回收。
### 可達(dá)性分析算法

可達(dá)性分析算法中,每個(gè)對(duì)象都有用于標(biāo)示該對(duì)象是否可達(dá)的標(biāo)記信息。以棧和static數(shù)據(jù)為根,從根出發(fā)跟隨所有的引用,就可以找到所有的可到達(dá)對(duì)象。一個(gè)可到達(dá)對(duì)象,一定被根或者其他可到達(dá)對(duì)象引用。不可達(dá)對(duì)象就是需要垃圾回收的對(duì)象。
#### 標(biāo)記清除算法(mark and sweep)
垃圾回收啟動(dòng)時(shí),Java程序暫停運(yùn)行,JVM從根出發(fā),找到所有的可到達(dá)對(duì)象并進(jìn)行標(biāo)記。然后,JVM掃描整個(gè)堆,找到剩余的不可到達(dá)對(duì)象,并清空這些對(duì)象占用的內(nèi)存。
缺點(diǎn)是:1、標(biāo)記和清除兩個(gè)過(guò)程效率都不高;2、標(biāo)記清除后會(huì)產(chǎn)生大量?jī)?nèi)存碎片,以后給較大對(duì)象分配內(nèi)存時(shí),找不到足夠的連續(xù)內(nèi)存空間。
#### 復(fù)制算法
復(fù)制算法下,將可用內(nèi)存劃分為大小相等的兩塊,每次只使用其中一塊。當(dāng)一塊使用完時(shí),將還存活的對(duì)象復(fù)制到另一塊內(nèi)存區(qū)域上,再把已使用過(guò)的那一塊一次清空。復(fù)制算法在對(duì)象存活率低時(shí)回收效率較高,缺點(diǎn)是內(nèi)存空間的使用率只有一半。
目前商業(yè)虛擬機(jī)采用這種算法來(lái)回收新生代,但并沒(méi)有按1:1來(lái)劃分,而是按8:1:1來(lái)劃分的。也就是一個(gè)eden區(qū),一個(gè)from區(qū)和一個(gè)to區(qū),這樣只有10%的內(nèi)存空間會(huì)被浪費(fèi)。
#### 標(biāo)記整理算法(copy and sweep)
使用復(fù)制算法當(dāng)對(duì)象存活率較高時(shí),效率就會(huì)變低。標(biāo)記整理算法在找到所有可達(dá)對(duì)象并標(biāo)記后, 將所有存活對(duì)象都向內(nèi)存區(qū)域的一端移動(dòng),然后清理掉邊界以外的所有內(nèi)存區(qū)域。
標(biāo)記整理算法多用于回收老年代。
#### 分代回收算法(generational collection)

堆分為三代:永久代(permanent)、老年代(tenured)、新生代(young)。新生代又分為三個(gè)區(qū)域,分別為:eden區(qū)、from區(qū)和to區(qū)(比例約為8:1:1)。
分代回收流程如下:
1、自從上次垃圾回收后創(chuàng)建的對(duì)象叫新生對(duì)象,存放于eden區(qū),當(dāng)eden區(qū)沒(méi)有空間再存放新生對(duì)象時(shí),會(huì)觸發(fā)一次Minor GC,JVM會(huì)采用復(fù)制算法將eden區(qū)和from區(qū)的所有可到達(dá)對(duì)象復(fù)制到to區(qū),to區(qū)緊密排列著復(fù)制來(lái)的對(duì)象,然后清空eden區(qū)和from區(qū)。經(jīng)過(guò)此番操作后from區(qū)和to區(qū)互調(diào)位置,也就是原來(lái)的from區(qū)變成to區(qū),to區(qū)變成from區(qū)。新生對(duì)象繼續(xù)存放在已經(jīng)清空的eden區(qū)中。
2、當(dāng)發(fā)現(xiàn)to區(qū)也已經(jīng)放不下eden區(qū)和from區(qū)的所有對(duì)象時(shí),會(huì)將部分對(duì)象放到老年代中。
3、即使to區(qū)沒(méi)有滿(mǎn),JVM也會(huì)移動(dòng)生命周期足夠久遠(yuǎn)的對(duì)象到老年代。
4、如果老年代存儲(chǔ)已滿(mǎn)無(wú)法放入新的對(duì)象時(shí),JVM會(huì)觸發(fā)Major GC,采用標(biāo)記整理算法對(duì)老年代進(jìn)行垃圾回收。
5、永久代中主要用于存放靜態(tài)文件,如Java類(lèi)、方法等,永久代對(duì)于垃圾回收沒(méi)有顯著影響,一般回收廢棄常量和無(wú)用的類(lèi)。
其中Minor GC發(fā)生頻率較高;而老年代對(duì)象由于存活時(shí)間較長(zhǎng),可到達(dá)標(biāo)記率高,因此Major GC發(fā)生頻率較低。
# 字節(jié)碼與機(jī)器碼

Java源代碼通過(guò)編譯變成字節(jié)碼(在Java程序中為.class文件,在Android程序中為.dex文件),然后字節(jié)碼按照模版中的規(guī)則解釋為機(jī)器碼(.oat文件),才可以被CPU解讀。
- 導(dǎo)讀
- Java知識(shí)
- Java基本程序設(shè)計(jì)結(jié)構(gòu)
- 【基礎(chǔ)知識(shí)】Java基礎(chǔ)
- 【源碼分析】Okio
- 【源碼分析】深入理解i++和++i
- 【專(zhuān)題分析】JVM與GC
- 【面試清單】Java基本程序設(shè)計(jì)結(jié)構(gòu)
- 對(duì)象與類(lèi)
- 【基礎(chǔ)知識(shí)】對(duì)象與類(lèi)
- 【專(zhuān)題分析】Java類(lèi)加載過(guò)程
- 【面試清單】對(duì)象與類(lèi)
- 泛型
- 【基礎(chǔ)知識(shí)】泛型
- 【面試清單】泛型
- 集合
- 【基礎(chǔ)知識(shí)】集合
- 【源碼分析】SparseArray
- 【面試清單】集合
- 多線程
- 【基礎(chǔ)知識(shí)】多線程
- 【源碼分析】ThreadPoolExecutor源碼分析
- 【專(zhuān)題分析】volatile關(guān)鍵字
- 【面試清單】多線程
- Java新特性
- 【專(zhuān)題分析】Lambda表達(dá)式
- 【專(zhuān)題分析】注解
- 【面試清單】Java新特性
- Effective Java筆記
- Android知識(shí)
- Activity
- 【基礎(chǔ)知識(shí)】Activity
- 【專(zhuān)題分析】運(yùn)行時(shí)權(quán)限
- 【專(zhuān)題分析】使用Intent打開(kāi)三方應(yīng)用
- 【源碼分析】Activity的工作過(guò)程
- 【面試清單】Activity
- 架構(gòu)組件
- 【專(zhuān)題分析】MVC、MVP與MVVM
- 【專(zhuān)題分析】數(shù)據(jù)綁定
- 【面試清單】架構(gòu)組件
- 界面
- 【專(zhuān)題分析】自定義View
- 【專(zhuān)題分析】ImageView的ScaleType屬性
- 【專(zhuān)題分析】ConstraintLayout 使用
- 【專(zhuān)題分析】搞懂點(diǎn)九圖
- 【專(zhuān)題分析】Adapter
- 【源碼分析】LayoutInflater
- 【源碼分析】ViewStub
- 【源碼分析】View三大流程
- 【源碼分析】觸摸事件分發(fā)機(jī)制
- 【源碼分析】按鍵事件分發(fā)機(jī)制
- 【源碼分析】Android窗口機(jī)制
- 【面試清單】界面
- 動(dòng)畫(huà)和過(guò)渡
- 【基礎(chǔ)知識(shí)】動(dòng)畫(huà)和過(guò)渡
- 【面試清單】動(dòng)畫(huà)和過(guò)渡
- 圖片和圖形
- 【專(zhuān)題分析】圖片加載
- 【面試清單】圖片和圖形
- 后臺(tái)任務(wù)
- 應(yīng)用數(shù)據(jù)和文件
- 基于網(wǎng)絡(luò)的內(nèi)容
- 多線程與多進(jìn)程
- 【基礎(chǔ)知識(shí)】多線程與多進(jìn)程
- 【源碼分析】Handler
- 【源碼分析】AsyncTask
- 【專(zhuān)題分析】Service
- 【源碼分析】Parcelable
- 【專(zhuān)題分析】Binder
- 【源碼分析】Messenger
- 【面試清單】多線程與多進(jìn)程
- 應(yīng)用優(yōu)化
- 【專(zhuān)題分析】布局優(yōu)化
- 【專(zhuān)題分析】繪制優(yōu)化
- 【專(zhuān)題分析】?jī)?nèi)存優(yōu)化
- 【專(zhuān)題分析】啟動(dòng)優(yōu)化
- 【專(zhuān)題分析】電池優(yōu)化
- 【專(zhuān)題分析】包大小優(yōu)化
- 【面試清單】應(yīng)用優(yōu)化
- Android新特性
- 【專(zhuān)題分析】狀態(tài)欄、ActionBar和導(dǎo)航欄
- 【專(zhuān)題分析】應(yīng)用圖標(biāo)、通知欄適配
- 【專(zhuān)題分析】Android新版本重要變更
- 【專(zhuān)題分析】唯一標(biāo)識(shí)符的最佳做法
- 開(kāi)源庫(kù)源碼分析
- 【源碼分析】BaseRecyclerViewAdapterHelper
- 【源碼分析】ButterKnife
- 【源碼分析】Dagger2
- 【源碼分析】EventBus3(一)
- 【源碼分析】EventBus3(二)
- 【源碼分析】Glide
- 【源碼分析】OkHttp
- 【源碼分析】Retrofit
- 其他知識(shí)
- Flutter
- 原生開(kāi)發(fā)與跨平臺(tái)開(kāi)發(fā)
- 整體歸納
- 狀態(tài)及狀態(tài)管理
- 零碎知識(shí)點(diǎn)
- 添加Flutter到現(xiàn)有應(yīng)用
- Git知識(shí)
- Git命令
- .gitignore文件
- 設(shè)計(jì)模式
- 創(chuàng)建型模式
- 結(jié)構(gòu)型模式
- 行為型模式
- RxJava
- 基礎(chǔ)
- Linux知識(shí)
- 環(huán)境變量
- Linux命令
- ADB命令
- 算法
- 常見(jiàn)數(shù)據(jù)結(jié)構(gòu)及實(shí)現(xiàn)
- 數(shù)組
- 排序算法
- 鏈表
- 二叉樹(shù)
- 棧和隊(duì)列
- 算法時(shí)間復(fù)雜度
- 常見(jiàn)算法思想
- 其他技術(shù)
- 正則表達(dá)式
- 編碼格式
- HTTP與HTTPS
- 【面試清單】其他知識(shí)
- 開(kāi)發(fā)歸納
- Android零碎問(wèn)題
- 其他零碎問(wèn)題
- 開(kāi)發(fā)思路
