特性總覽
以下是 Java 7 中引入的部分新特性
-
java.lang包- Java 7 多線程下自定義類加載器的優(yōu)化
-
Java 語(yǔ)言特性
- 改進(jìn)的類型推斷;
- 使用
try-with-resources進(jìn)行自動(dòng)資源管理 -
switch支持String; -
catch多個(gè)異常; - 數(shù)字格式增強(qiáng)(允許數(shù)字字面量下劃線分割);
- 二進(jìn)制字面量;
- 增強(qiáng)的文件系統(tǒng);
-
Fork/Join框架;
-
Java 虛擬機(jī) (JVM)
- 提供新的 G1 收集器;
- 加強(qiáng)對(duì)動(dòng)態(tài)調(diào)用的支持;
- 新增分層編譯支持;
- 壓縮 Oops;
- 其他優(yōu)化;
其他;
多線程下自定義類加載器的優(yōu)化
在 Java 7 之前,某些情況下的自定義類加載器容易出現(xiàn)死鎖問(wèn)題。
// 類的繼承情況:
class A extends B
class C extends D
// 類加載器:
Custom Classloader CL1:
直接加載類 A
委托 CL2 加載類 B
Custom Classloader CL2:
直接加載類 C
委托 CL1 加載類 D
// 多線程下的情況:
Thread 1:
使用 CL1 加載類 A
→ 定義類 A 的時(shí)候會(huì)觸發(fā) loadClass(B),這時(shí)會(huì)嘗試 鎖住?? CL2
Thread 2:
使用 CL2 加載類 C
→ 定義 C 的時(shí)候會(huì)觸發(fā) loadClass(D),這時(shí)會(huì)嘗試 鎖住?? CL1
?? 造成 死鎖??
復(fù)制代碼
造成死鎖的重要原因出在 JDK 默認(rèn)的 java.lang.ClassLoader.loadClass() 方法上:

可以看到,JDK 6 及之前的 loadClass() 的 synchronized 關(guān)鍵字是加在方法級(jí)別的,那么這就意味加載類時(shí)獲取到的是一個(gè) ClassLoader 級(jí)別的鎖。
我們來(lái)描述一下死鎖產(chǎn)生的情況:

文字版的描述如下:
線程1:CL1 去 loadClass(A) 獲取到了 CL1 對(duì)象鎖,因?yàn)?A 繼承了類 B,defineClass(A) 會(huì)觸發(fā) loadClass(B),嘗試獲取 CL2 對(duì)象鎖;
線程2:CL2 去 loadClass(C) 獲取到了 CL2 對(duì)象鎖,因?yàn)?C 繼承了類 D,defineClass(C) 會(huì)觸發(fā) loadClass(D),嘗試獲取 CL1 對(duì)象鎖
線程1 嘗試獲取 CL2 對(duì)象鎖的時(shí)候,CL2 對(duì)象鎖已經(jīng)被 線程2 拿到了,那么 線程1 等待 線程2 釋放 CL2 對(duì)象鎖。
線程2 嘗試獲取 CL1 對(duì)像鎖的時(shí)候,CL1 對(duì)像鎖已經(jīng)被 線程1 拿到了,那么 線程2 等待 線程1 釋放 CL1 對(duì)像鎖。
然后兩個(gè)線程一直在互相等中…從而產(chǎn)生了死鎖現(xiàn)象...
究其原因就是因?yàn)?ClassLoader 的鎖太粗粒度了。在 Java 7 中,在使用具有并行功能的類加載器的時(shí)候,將專門(mén)用一個(gè)帶有 類加載器和類名稱組合的對(duì)象 用于進(jìn)行同步操作。(感興趣可以看一下 loadClass() 內(nèi)部的 getClassLoadingLock(name) 方法)
Java 7 之后,之前線程死鎖的情況將不存在:
線程1:
使用CL1加載類A(鎖定CL1 + A)
defineClass A觸發(fā)
loadClass B(鎖定CL2 + B)
線程2:
使用CL2加載類C(鎖定CL2 + C)
defineClass C觸發(fā)
loadClass D(鎖定CL1 + D)
改進(jìn)的類型推斷
在 Java 7 之前,使用泛型時(shí),您必須為變量類型及其實(shí)際類型提供類型參數(shù):
Map<String, List<String>> map = new HashMap<String, List<String>>();
在 Java 7 之后,編譯器可以通過(guò)識(shí)別空白菱形推斷出在聲明在左側(cè)定義的類型:
Map<String, List<String>> map = new HashMap<>();
自動(dòng)資源管理
在 Java 7 之前,我們必須使用 finally 塊來(lái)清理資源,但防止系統(tǒng)崩壞的清理資源的操作并不是強(qiáng)制性的。在 Java 7 中,我們無(wú)需顯式的資源清理,它允許我們使用 try-with-resrouces 語(yǔ)句來(lái)借由 JVM 自動(dòng)完成清理工作。
Java 7 之前:
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(path));
return br.readLine();
} catch (Exception e) {
log.error("BufferedReader Exception", e);
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e) {
log.error("BufferedReader close Exception", e);
}
}
}
Java 7 及之后的寫(xiě)法:
try (BufferedReader br = new BufferedReader(new FileReader(path)) {
return br.readLine();
} catch (Exception e) {
log.error("BufferedReader Exception", e);
}
switch 支持 String
switch 在 Java 7 中能夠接受 String 類型的參數(shù),實(shí)例如下:
String s = ...
switch(s) {
case "condition1":
processCondition1(s);
break;
case "condition2":
processCondition2(s);
break;
default:
processDefault(s);
break;
}
catch 多個(gè)異常
自Java 7開(kāi)始,catch 中可以一次性捕捉多個(gè)異常做統(tǒng)一處理。示例如下:
public void handle() {
ExceptionThrower thrower = new ExceptionThrower();
try {
thrower.manyExceptions();
} catch (ExceptionA | ExceptionB ab) {
System.out.println(ab.getClass());
} catch (ExceptionC c) {
System.out.println(c.getClass());
}
}
請(qǐng)注意:如果 catch 塊處理多個(gè)異常類型,則 catch 參數(shù)隱式為 final 類型,這意味著,您不能在 catch 塊中為其分配任何值。
數(shù)字格式增強(qiáng)
為了解決長(zhǎng)數(shù)字可讀性不好的問(wèn)題,在 Java 7 中支持了使用下劃線分割的數(shù)字表達(dá)形式:
/**
* Supported in int
* */
int improvedInt = 10_00_000;
/**
* Supported in float
* */
float improvedFloat = 10_00_000f;
/**
* Supported in long
* */
float improvedLong = 10_00_000l;
/**
* Supported in double
* */
float improvedDouble = 10_00_000;
二進(jìn)制字面量
在 Java 7 中,您可以使用整型類型 (byte、short、int、long) 并加上前綴 0b (或 0B) 來(lái)創(chuàng)建二進(jìn)制字面量。這在 Java 7 之前,您只能使用八進(jìn)制值 (前綴為 0) 或十六進(jìn)制值 (前綴為 0x 或者 0X) 來(lái)創(chuàng)建:
int sameVarOne = 0b01010000101;
int sameVarTwo = 0B01_010_000_101;
byte byteVar = (byte) 0b01010000101;
short shortVar = (short) 0b01010000101
增強(qiáng)的文件系統(tǒng)
Java 7 推出了全新的NIO 2.0 API以此改變針對(duì)文件管理的不便,使得在java.nio.file包下使用Path、Paths、Files、WatchService、FileSystem等常用類型可以很好的簡(jiǎn)化開(kāi)發(fā)人員對(duì)文件管理的編碼工作。
1 - Path 接口 和 Paths 類
Path接口的某些功能其實(shí)可以和java.io包下的File類等價(jià),當(dāng)然這些功能僅限于只讀操作。在實(shí)際開(kāi)發(fā)過(guò)程中,開(kāi)發(fā)人員可以聯(lián)用Path接口和Paths類,從而獲取文件的一系列上下文信息。
- int getNameCount(): 獲取當(dāng)前文件節(jié)點(diǎn)數(shù)
- Path getFileName(): 獲取當(dāng)前文件名稱
- Path getRoot(): 獲取當(dāng)前文件根目錄
- Path getParent(): 獲取當(dāng)前文件上級(jí)關(guān)聯(lián)目錄
聯(lián)用Path接口和Paths類型獲取文件信息:
Path path = Paths.get("G:/test/test.xml");
System.out.println("文件節(jié)點(diǎn)數(shù):" + path.getNameCount());
System.out.println("文件名稱:" + path.getFileName());
System.out.println("文件根目錄:" + path.getRoot());
System.out.println("文件上級(jí)關(guān)聯(lián)目錄:" + path.getParent());
2 - Files 類
聯(lián)用Path接口和Paths類可以很方便的訪問(wèn)到目標(biāo)文件的上下文信息。當(dāng)然這些操作全都是只讀的,如果開(kāi)發(fā)人員想對(duì)文件進(jìn)行其它非只讀操作,比如文件的創(chuàng)建、修改、刪除等操作,則可以使用Files類型進(jìn)行操作。
Files類型常用方法如下:
- Path createFile(): 在指定的目標(biāo)目錄創(chuàng)建新文件
- void delete(): 刪除指定目標(biāo)路徑的文件或文件夾
- Path copy(): 將指定目標(biāo)路徑的文件拷貝到另一個(gè)文件中
- Path move(): 將指定目標(biāo)路徑的文件轉(zhuǎn)移到其他路徑下,并刪除源文件
使用Files類型復(fù)制、粘貼文件示例:
Files.copy(Paths.get("/test/src.xml"), Paths.get("/test/target.xml"));
使用 Files 類型來(lái)管理文件,相對(duì)于傳統(tǒng)的 I/O 方式來(lái)說(shuō)更加方便和簡(jiǎn)單。因?yàn)榫唧w的操作實(shí)現(xiàn)將全部移交給 NIO 2.0 API,開(kāi)發(fā)人員則無(wú)需關(guān)注。
3 - WatchService
Java 7 還為開(kāi)發(fā)人員提供了一套全新的文件系統(tǒng)功能,那就是文件監(jiān)測(cè)。 在此或許有很多朋友并不知曉文件監(jiān)測(cè)有何意義及目,那么請(qǐng)大家回想下調(diào)試成熱發(fā)布功能后的 Web 容器。當(dāng)項(xiàng)目迭代后并重新部署時(shí),開(kāi)發(fā)人員無(wú)需對(duì)其進(jìn)行手動(dòng)重啟,因?yàn)?Web 容器一旦監(jiān)測(cè)到文件發(fā)生改變后,便會(huì)自動(dòng)去適應(yīng)這些“變化”并重新進(jìn)行內(nèi)部裝載。Web 容器的熱發(fā)布功能同樣也是基于文件監(jiān)測(cè)功能,所以不得不承認(rèn),文件監(jiān)測(cè)功能的出現(xiàn)對(duì)于 Java 文件系統(tǒng)來(lái)說(shuō)是具有重大意義的。
文件監(jiān)測(cè)是基于事件驅(qū)動(dòng)的,事件觸發(fā)是作為監(jiān)測(cè)的先決條件。開(kāi)發(fā)人員可以使用java.nio.file包下的StandardWatchEventKinds類型提供的3種字面常量來(lái)定義監(jiān)測(cè)事件類型,值得注意的是監(jiān)測(cè)事件需要和WatchService實(shí)例一起進(jìn)行注冊(cè)。
StandardWatchEventKinds類型提供的監(jiān)測(cè)事件:
- ENTRY_CREATE:文件或文件夾新建事件;
- ENTRY_DELETE:文件或文件夾刪除事件;
- ENTRY_MODIFY:文件或文件夾粘貼事件;
使用WatchService類實(shí)現(xiàn)文件監(jiān)控完整示例:
public static void testWatch() {
/* 監(jiān)控目標(biāo)路徑 */
Path path = Paths.get("G:/");
try {
/* 創(chuàng)建文件監(jiān)控對(duì)象. */
WatchService watchService = FileSystems.getDefault().newWatchService();
/* 注冊(cè)文件監(jiān)控的所有事件類型. */
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
/* 循環(huán)監(jiān)測(cè)文件. */
while (true) {
WatchKey watchKey = watchService.take();
/* 迭代觸發(fā)事件的所有文件 */
for (WatchEvent<?> event : watchKey.pollEvents()) {
System.out.println(event.context().toString() + " 事件類型:" + event.kind());
}
if (!watchKey.reset()) {
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
通過(guò)上述程序示例我們可以看出,使用WatchService接口進(jìn)行文件監(jiān)控非常簡(jiǎn)單和方便。首先我們需要定義好目標(biāo)監(jiān)控路徑,然后調(diào)用FileSystems類型的newWatchService()方法創(chuàng)建WatchService對(duì)象。接下來(lái)我們還需使用Path接口的register()方法注冊(cè)WatchService實(shí)例及監(jiān)控事件。當(dāng)這些基礎(chǔ)作業(yè)層全部準(zhǔn)備好后,我們?cè)倬帉?xiě)外圍實(shí)時(shí)監(jiān)測(cè)循環(huán)。最后迭代WatchKey來(lái)獲取所有觸發(fā)監(jiān)控事件的文件即可。
Fork/ Join 框架
1 - 什么是 Fork/ Join 框架
Java 7 提供的一個(gè)用于并行執(zhí)行任務(wù)的框架,是一個(gè)把大任務(wù)分割成若干個(gè)小任務(wù),最終匯總每個(gè)小任務(wù)結(jié)果后得到大任務(wù)結(jié)果的框架。比如我們要計(jì)算 1 + 2 + .....+ 10000,就可以分割成 10 個(gè)子任務(wù),讓每個(gè)子任務(wù)分別對(duì) 1000 個(gè)數(shù)進(jìn)行運(yùn)算,最終匯總這 10 個(gè)子任務(wù)的結(jié)果。
Fork/Join 的運(yùn)行流程圖如下:

2 - 工作竊取算法
工作竊取 (work-stealing) 算法是指某個(gè)線程從其他隊(duì)列里竊取任務(wù)來(lái)執(zhí)行。核心思想是:自己的活干完了去看看別人有沒(méi)有沒(méi)有干完的活兒,如果有就拿過(guò)來(lái)幫他干。
工作竊取的運(yùn)行流程圖如下:

工作竊取算法的優(yōu)點(diǎn)是充分利用線程進(jìn)行并行計(jì)算,并減少了線程間的競(jìng)爭(zhēng),其缺點(diǎn)是在某些情況下還是存在競(jìng)爭(zhēng),比如雙端隊(duì)列里只有一個(gè)任務(wù)時(shí)。并且消耗了更多的系統(tǒng)資源,比如創(chuàng)建多個(gè)線程和多個(gè)雙端隊(duì)列。
3 - 簡(jiǎn)單示例
讓我們通過(guò)一個(gè)簡(jiǎn)單的需求來(lái)使用下Fork/Join框架,需求是:計(jì)算1 + 2 + 3 + 4的結(jié)果。
使用Fork/Join框架首先要考慮到的是如何分割任務(wù),如果我們希望每個(gè)子任務(wù)最多執(zhí)行兩個(gè)數(shù)的相加,那么我們?cè)O(shè)置分割的閾值是2,由于是4個(gè)數(shù)字相加,所以Fork/Join框架會(huì)把這個(gè)任務(wù)fork成兩個(gè)子任務(wù),子任務(wù)一負(fù)責(zé)計(jì)算1 + 2,子任務(wù)二負(fù)責(zé)計(jì)算3 + 4,然后再join兩個(gè)子任務(wù)的結(jié)果。
因?yàn)槭怯薪Y(jié)果的任務(wù),所以必須繼承RecursiveTask,實(shí)現(xiàn)代碼如下:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
/**
* CountTask.
*
* @author blinkfox on 2018-01-03.
* @originalRef http://blinkfox.com/2018/11/12/hou-duan/java/java7-xin-te-xing-ji-shi-yong/#toc-heading-5
*/
public class CountTask extends RecursiveTask<Integer> {
/** 閾值. */
public static final int THRESHOLD = 2;
/** 計(jì)算的開(kāi)始值. */
private int start;
/** 計(jì)算的結(jié)束值. */
private int end;
/**
* 構(gòu)造方法.
*
* @param start 計(jì)算的開(kāi)始值
* @param end 計(jì)算的結(jié)束值
*/
public CountTask(int start, int end) {
this.start = start;
this.end = end;
}
/**
* 執(zhí)行計(jì)算的方法.
*
* @return int型結(jié)果
*/
@Override
protected Integer compute() {
int sum = 0;
// 如果任務(wù)足夠小就計(jì)算任務(wù).
if ((end - start) <= THRESHOLD) {
for (int i = start; i <= end; i++) {
sum += i;
}
} else {
// 如果任務(wù)大于閾值,就分裂成兩個(gè)子任務(wù)來(lái)計(jì)算.
int middle = (start + end) / 2;
CountTask leftTask = new CountTask(start, middle);
CountTask rightTask = new CountTask(middle + 1, end);
// 等待子任務(wù)執(zhí)行完,并得到結(jié)果,再合并執(zhí)行結(jié)果.
leftTask.fork();
rightTask.fork();
sum = leftTask.join() + rightTask.join();
}
return sum;
}
/**
* main方法.
*
* @param args 數(shù)組參數(shù)
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
ForkJoinPool fkPool = new ForkJoinPool();
CountTask task = new CountTask(1, 4);
Future<Integer> result = fkPool.submit(task);
System.out.println("result:" + result.get());
}
}
虛擬機(jī)增強(qiáng)
1 - 提供新的 G1 收集器
Java 7 引入了一個(gè)被稱為 Garbage-First (G1) 的垃圾收集器。G1 是服務(wù)器式的垃圾收集器 (設(shè)計(jì)初衷是盡量縮短處理超大堆——大于 4GB——時(shí)產(chǎn)生的停頓),適用于具有大內(nèi)存多處理器的計(jì)算機(jī)。
與之前收集器不同的是 G1 沒(méi)有使用 Java 7 之前連續(xù)的內(nèi)存模型:

而是將整個(gè) 堆空間 劃分為了多個(gè)大小相等的獨(dú)立區(qū)域 (Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔閡了,它們都是一部分 (可以不連續(xù)) Region的集合:

G1 完全可以預(yù)測(cè)停頓時(shí)間,并且可以為內(nèi)存密集型應(yīng)用程序提供更高的吞吐量。
2 - 加強(qiáng)對(duì)動(dòng)態(tài)調(diào)用的支持
Java 7 之前字節(jié)碼指令集中,四條方法調(diào)用指令 (invokevirtual、invokespeicial、invokestatic、invokeinterface) 的第一個(gè)參數(shù)都是 被調(diào)用方法的符號(hào)引用,但動(dòng)態(tài)類型的語(yǔ)言只有在 運(yùn)行期 才能確定接受的參數(shù)類型。這樣,在 Java 虛擬機(jī)上實(shí)現(xiàn)的動(dòng)態(tài)類型語(yǔ)言就不得不使用“曲線救國(guó)”的方式 (如編譯時(shí)留個(gè)占位符類型,運(yùn)行時(shí)動(dòng)態(tài)生成字節(jié)碼實(shí)現(xiàn)具體類型到占位符類型的適配) 來(lái)實(shí)現(xiàn),這樣勢(shì)必讓動(dòng)態(tài)類型語(yǔ)言實(shí)現(xiàn)的復(fù)雜度增加,也可能帶來(lái)額外的性能或者內(nèi)存開(kāi)銷。
為了從 JVM 底層解決這個(gè)問(wèn)題 (早在 1997 年出版的《Java 虛擬機(jī)規(guī)范》第一版中就規(guī)劃了這樣一個(gè)愿景:“在未來(lái),我們會(huì)對(duì) Java 虛擬機(jī)進(jìn)行適當(dāng)?shù)臄U(kuò)展,以便更好的支持其他語(yǔ)言運(yùn)行于 Java 虛擬機(jī)之上”), Java 7 新引入了 invokedynamic 指令以及 java.lang.invoke 包。
3 - 分層編譯
Java 7 中引入的 分層編譯 為服務(wù)器 VM 帶來(lái)了客戶端一般的啟動(dòng)速度。通常,服務(wù)器 VM 使用 解釋器 來(lái)收集有關(guān)「提供給 編譯器 的方法」的分析信息。在分層模式中,除了 解釋器 之外,客戶端編譯器 還用于生成方法的編譯版本,這些方法收集關(guān)于自身的分析信息。由于編譯后的代碼比 解釋器 要快得多,程序在分析階段執(zhí)行時(shí)會(huì)有更好的性能。在許多情況下,可以實(shí)現(xiàn)比客戶機(jī) VM 更快的啟動(dòng),因?yàn)榉?wù)器編譯器生成的最終代碼可能在應(yīng)用程序初始化的早期階段就已經(jīng)可用了。分層模式還可以獲得比常規(guī)服務(wù)器 VM 更好的峰值性能,因?yàn)楦斓姆治鲭A段允許更長(zhǎng)的分析周期,這可能產(chǎn)生更好的優(yōu)化。(ps: 官方文檔如是說(shuō)...)
支持 32 位和 64 位模式,以及壓縮 Oops。在 java 命令中使用 -XX:+TieredCompilation 標(biāo)志來(lái)啟用分層編譯。
(ps: 這在 Java 8 是默認(rèn)開(kāi)啟的)
4 - 壓縮 Oops (CompressOops)
HotSpot JVM 使用名為 oops 或 Ordinary Object Pointers 的數(shù)據(jù)結(jié)構(gòu)來(lái)表示對(duì)象。這些 oops 等同于本地C指針。 instanceOops 是一種特殊的 oop,表示 Java 中的對(duì)象實(shí)例。
在 32 位的系統(tǒng)中,對(duì)象頭指針占 4 字節(jié),只能引用 4 GB 的內(nèi)存,在 64 位系統(tǒng)中,對(duì)象頭指針占 8 字節(jié)。更大的指針尺寸帶來(lái)了問(wèn)題:
更容易 GC,因?yàn)檎加每臻g更大了;
降低了 CPU 緩存命中率,因?yàn)橐粭l cache line 中能存放的指針數(shù)變少了;
為了能夠保持 32 位的性能,oop 必須保留 32 位。那么,如何用 32 位 oop 來(lái)引用更大的堆內(nèi)存呢?答案是——壓縮指針 (CompressedOops)。JVM 被設(shè)計(jì)為硬件友好,對(duì)象都是按照 8 字節(jié)對(duì)齊填充的,這意味著使用指針時(shí)的偏移量只會(huì)是 8 的倍數(shù),而不會(huì)是下面中的 1-7,只會(huì)是 0 或者 8:
mem: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
^ ^
這就允許了我們不再保留所有的引用,而是每隔 8 個(gè)字節(jié)保存一個(gè)引用:
mem: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
^ ^
| ___________________________|
| |
heap: | 0 | 1 |
CompressedOops,可以讓跑在 64 位平臺(tái)下的 JVM,不需要因?yàn)楦鼘挼膶ぶ?,而付?Heap 容量損失的代價(jià) (其中還涉及零基壓縮優(yōu)化——Zero-Based Compressed OOPs 技術(shù))。 不過(guò)它的實(shí)現(xiàn)方式是在機(jī)器碼中植入壓縮與解壓指令,可能會(huì)給 JVM 增加額外的開(kāi)銷。
其他優(yōu)化
將 interned 字符串移出 perm gen
在 JDK 7 中,interned 字符串不再在 Java 堆的永久生成中分配,而是在 Java 堆的主要部分 (稱為年輕代和年老代) 中分配,與應(yīng)用程序創(chuàng)建的其他對(duì)象一起分配。這一更改將導(dǎo)致駐留在主 Java 堆中的數(shù)據(jù)更多,而駐留在永久生成中的數(shù)據(jù)更少,因此可能需要調(diào)整堆大小。由于這一變化,大多數(shù)應(yīng)用程序在堆使用方面只會(huì)看到相對(duì)較小的差異,但加載許多類或大量使用 String.intern() 方法的較大應(yīng)用程序?qū)⒖吹礁@著的差異。
(ps: String.intern() 方法是運(yùn)行期擴(kuò)展方法區(qū)常量池的一種手段)
NUMA 收集器增強(qiáng)
Java 7 對(duì) Parallel Scavenger 垃圾收集器進(jìn)行了擴(kuò)展,以利用具有 NUMA (非統(tǒng)一內(nèi)存訪問(wèn)) 體系結(jié)構(gòu)的計(jì)算機(jī)的優(yōu)勢(shì)。大多數(shù)現(xiàn)代計(jì)算機(jī)都基于 NUMA 架構(gòu),在這種架構(gòu)中,訪問(wèn)內(nèi)存的不同部分需要花費(fèi)不同的時(shí)間。通常,系統(tǒng)中的每個(gè)處理器都具有提供低訪問(wèn)延遲和高帶寬的本地內(nèi)存,以及訪問(wèn)速度相當(dāng)慢的遠(yuǎn)程內(nèi)存。
在 Java HotSpot 虛擬機(jī)中,已實(shí)現(xiàn)了 NUMA 感知的分配器,以利用此類系統(tǒng)并為 Java 應(yīng)用程序提供自動(dòng)內(nèi)存放置優(yōu)化。
分配器控制堆的年輕代的 eden 空間,在其中創(chuàng)建大多數(shù)新對(duì)象。
分配器將空間劃分為多個(gè)區(qū)域,每個(gè)區(qū)域都放置在特定節(jié)點(diǎn)的內(nèi)存中。
分配器基于以下假設(shè):分配對(duì)象的線程將最有可能使用該對(duì)象。
為了確保最快地訪問(wèn)新對(duì)象,分配器將其放置在分配線程本地的區(qū)域中。
可以動(dòng)態(tài)調(diào)整區(qū)域的大小,以反映在不同節(jié)點(diǎn)上運(yùn)行的應(yīng)用程序線程的分配率。
這甚至可以提高單線程應(yīng)用程序的性能。另外,年輕一代,老一代和永久一代的“從”和“到”幸存者空間為其打開(kāi)了頁(yè)面交錯(cuò)。這樣可以確保所有線程平均平均具有對(duì)這些空間的相等的訪問(wèn)延遲。
版本號(hào)大于 50 的類文件必須使用 typechecker 進(jìn)行驗(yàn)證
從 Java 6 開(kāi)始,Oracle 的編譯器使用 StackMapTable 制作類文件。基本思想是,編譯器可以顯式指定對(duì)象的類型,而不是讓運(yùn)行時(shí)執(zhí)行此操作。這樣可以在運(yùn)行時(shí)提供極小的加速,以換取編譯期間的一些額外時(shí)間和已編譯的類文件 (前面提到的 StackMapTable) 中的某些復(fù)雜性。
作為一項(xiàng)實(shí)驗(yàn)功能,Java 6 編譯器默認(rèn)未啟用它。 如果不存在 StackMapTable,則運(yùn)行時(shí)默認(rèn)會(huì)驗(yàn)證對(duì)象類型本身。
版本號(hào)為 51 的類文件 (也就是 Java 7 的類文件) 是使用類型檢查驗(yàn)證程序?qū)iT(mén)驗(yàn)證的,因此,方法在適當(dāng)時(shí)必須具有 StackMapTable 屬性。對(duì)于版本 50 的類文件,如果文件中的堆棧映射丟失或不正確,則 HotSpot JVM 將故障轉(zhuǎn)移到類型推斷驗(yàn)證程序。對(duì)于版本為 51 (JDK 7 默認(rèn)版本) 的類文件,不會(huì)發(fā)生此故障轉(zhuǎn)移行為。