特點(diǎn)
程序計(jì)數(shù)器是一個(gè)以線程私有的一塊較小的內(nèi)存空間芭梯,用于記錄所屬線程所執(zhí)行的字節(jié)碼的行號指示器;字節(jié)碼解釋器工作時(shí)弄喘,通過改變程序計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令玖喘,分支、循環(huán)蘑志、跳準(zhǔn)累奈、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴程序計(jì)數(shù)器來完成急但。
在多線程中澎媒,就會存在線程上下文切換(CPU 時(shí)間片[1])執(zhí)行,為了線程切換后能恢復(fù)正確的執(zhí)行位置波桩,所以需要從程序計(jì)數(shù)器中獲取該線程需要執(zhí)行的字節(jié)碼的偏移地址(簡單來說戒努,可以先理解為執(zhí)行的代碼行號,但實(shí)際并不是所看到的代碼行號镐躲,后續(xù)學(xué)習(xí)了字節(jié)碼指令即明白了)柏卤。程序計(jì)數(shù)器是具備線程隔離性,每個(gè)線程工作時(shí)都有屬于自己的獨(dú)立程序計(jì)數(shù)器匀油。
-
如果線程執(zhí)行 Java 方法缘缚,程序計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址。如果執(zhí)行 Navtive 方法敌蚜,程序計(jì)數(shù)器值則為空(Undefined)桥滨。因?yàn)?Navtive 方法是 Java 通過 JNI 直接調(diào)用本地 C/C++ 庫,可以認(rèn)為是 Native 方法相當(dāng)于 C/C++ 暴露給 Java 的一個(gè)接口弛车,Java 通過調(diào)用這個(gè)接口從而調(diào)用到 C/C++ 方法齐媒。由于該方法是通過 C/C++ 而不是 Java 進(jìn)行實(shí)現(xiàn)。那么自然無法產(chǎn)生相應(yīng)的字節(jié)碼纷跛,并且 C/C++ 執(zhí)行時(shí)的內(nèi)存分配是由自己語言決定的喻括,而不是由 JVM 決定的。
由于是線程私有的贫奠,生命周期隨著線程唬血,線程啟動而產(chǎn)生望蜡,線程結(jié)束而消亡。
Java 虛擬機(jī)規(guī)范里面拷恨, 唯一 一個(gè)沒有規(guī)定任何 OutOfMemoryError 情況的區(qū)域脖律,由于保存的是線程需要執(zhí)行的字節(jié)碼的偏移地址,當(dāng)執(zhí)行下一條指令的時(shí)候腕侄,改變的只是程序計(jì)數(shù)器中保存的地址小泉,并不需要申請新的內(nèi)存來保存新的指令地址,因此冕杠,不會產(chǎn)生內(nèi)存溢出微姊。
答疑
可能有人對字節(jié)碼的偏移地址有所困惑,因?yàn)檫@個(gè)屬于字節(jié)碼指令的知識范疇分预,這里就簡單舉例讓大家先了解一下:
public int test() {
int x = 0;
int y = 1;
return x + y;
}
這段代碼轉(zhuǎn)化成字節(jié)碼指令又是這樣子的呢柒桑?可以使用 javap -v 命令執(zhí)行該類,生成出來的字節(jié)碼指令如下:
public int test();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_1
2: iconst_1
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: ireturn
LineNumberTable:
line 7: 0
line 8: 2
line 9: 4
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this Lcom/alibaba/uc/TestClass;
2 6 1 x I
4 4 2 y I
以上只是這個(gè)方法的字節(jié)碼指令噪舀,但是魁淳,我們重點(diǎn)所看的程序計(jì)數(shù)器所記錄的值是:如 7: ireturn
操作指令中的 7 即為偏移地址。
偏移地址: 操作指令
0: iconst_0
1: istore_1
2: iconst_1
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: ireturn
-
CPU 時(shí)間片
CPU 時(shí)間片即 CPU 分配給各個(gè)程序的時(shí)間与倡,每個(gè)線程被分配一個(gè)時(shí)間段界逛,稱作它的時(shí)間片,即該進(jìn)程允許運(yùn)行的時(shí)間纺座,使各個(gè)程序從表面上看是同時(shí)進(jìn)行的息拜。如果在時(shí)間片結(jié)束時(shí)進(jìn)程還在運(yùn)行,則 CPU 將被剝奪并分配給另一個(gè)進(jìn)程净响。如果進(jìn)程在時(shí)間片結(jié)束前阻塞或結(jié)束少欺,則 CPU 當(dāng)即進(jìn)行切換。而不會造成 CPU 資源浪費(fèi)馋贤。在宏觀上:我們可以同時(shí)打開多個(gè)應(yīng)用程序赞别,每個(gè)程序并行不悖,同時(shí)運(yùn)行配乓。但在微觀上:由于只有一個(gè) CPU仿滔,一次只能處理程序要求的一部分,如何處理公平犹芹,一種方法就是引入時(shí)間片崎页,每個(gè)程序輪流執(zhí)行。 ?