Java8 中不起眼的新特性
Metaspace
一直以來,JVM 都在heap 中管理這一個稱為PermGen的區域,用于放置Class對象實例, 靜態變量, String pool等。PermGen的存在對于GC 有很大的影響,存在于PermGen 中的對象很少能被回收,一旦PermGen 滿了就會觸發Stop-the-world 的Full GC——即使是設置使用了CMS 等延遲更低的垃圾收集器也是如此。一旦垃圾回收也不能會受到足夠的內存,OOM Error就會被引發,終止程序的執行。使用了動態字節碼生產技術的時候(asm, 動態代理, groovy等),由于需要生產很多動態類,這種情況更為嚴重。
在Java7 中, String pool 被移出了PermGen。到了Java8,PermGen 被徹底取消,Class 對象等元數據被放到了新的空間Metaspace 中。Metaspace 不存在于JVM Heap中,不受GC 管轄,也降低了GC 的壓力。由于只存儲class 元數據,所以數據的移除策略也很簡單,當沒有這個class 的實例存在后,就可以移除。
64位的server JVM 中,Metaspace 初始大小為21M。當Metaspace 滿了之后,依然會觸發GC,同時會調大Metaspace 空間。如果必要的話,可以通過–XX:MetaspaceSize 來設置初始的大小,避免引發GC。
Stamped Locks
時間戳鎖。時間戳鎖是一種樂觀鎖,它維持一個序號,每個更新操作都會將將序號增加。在起始的時候,記錄一個序號,在訪問完成之后,如果這個序號沒有改變,就認為這段時間沒有訪問沖突。這是一種性能極高的加鎖方式。
long stamp = lock.tryOptimisticRead(); work; if (lock.validate(stamp)){ //success! no contention with a writer thread } else { // 訪問沖突, 直接fail back 到普通的鎖 stamp = lock.readLock(); try { //no writing happening now work(); } finally { lock.unlock(stamp); } }
Concurrent Adders 是Atomic類的進化版本。原子類通過不斷的cas 自旋,來實現原子的更新。如果競爭激烈的情況下,會一直進行cas 操作直到成功,消耗CPU 資源。
Concurrent Adders 為每個線程保存一個本地的增量。在cas 自旋失敗之后,直接將要add 的增量寫到線程本地的增量中。當要讀取的時候,在取出數據的基礎上,再加上本線程以及其他線程的增量,從而得到一個最終正確的數值。
除非有特殊原因,使用Concurrent Adders 代替原子類是一個很好的選擇。
Parallel Sorting
增加了充分利用多核的排序方法:
String joined = new StringJoiner("-") .add("foo") .add("bar") .add("baz") .toString(); // "foo-bar-baz"
這個是從scala 等函數式編程語言借鑒來的東西,在函數式編程語言中,Optional 用來消除null 引起的副作用,是一種Monad 的方式。Guava 等lib 已經有了類似的工具,現在Java 把它引入到了標準庫中。Optional 是一個泛型類,它可以hold一個null 或者非null 的value,提供get(), orElse(), ifPresent() 等方法。
Optional<User> user = tryFindUser(int userID); user.ifPresent(System.out::print);