Java12的新特性

go4it發表於2019-03-20

Java語言特性系列

本文主要講述一下Java12的新特性

版本號

java -version
openjdk version "12" 2019-03-19
OpenJDK Runtime Environment (build 12+33)
OpenJDK 64-Bit Server VM (build 12+33, mixed mode)
複製程式碼

從version資訊可以看出是build 12+33

特性列表

Shenandoah GC是一個面向low-pause-time的垃圾收集器,它最初由Red Hat實現,支援aarch64及amd64 architecture;ZGC也是面向low-pause-time的垃圾收集器,不過ZGC是基於colored pointers來實現,而Shenandoah GC是基於brooks pointers來實現;如果要使用Shenandoah GC需要編譯時--with-jvm-features選項帶有shenandoahgc,然後啟動時使用-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

在jdk原始碼裡頭新增了一套基礎的microbenchmarks suite

對switch進行了增強,除了使用statement還可以使用expression,比如原來的寫法如下:

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}
複製程式碼

現在可以改為如下寫法:

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}
複製程式碼

以及在表示式返回值

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
};
複製程式碼

對於需要返回值的switch expression要麼正常返回值要麼丟擲異常,以下這兩種寫法都是錯誤的

int i = switch (day) {
    case MONDAY -> {
        System.out.println("Monday"); 
        // ERROR! Block doesn't contain a break with value
    }
    default -> 1;
};
i = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY: 
        break 0;
    default: 
        System.out.println("Second half of the week");
        // ERROR! Group doesn't contain a break with value
};
複製程式碼

新增了JVM Constants API,具體來說就是java.base模組新增了java.lang.constant包,引入了ConstantDesc介面(ClassDesc、MethodTypeDesc、MethodHandleDesc這幾個介面直接繼承了ConstantDesc介面)以及Constable介面;ConstantDesc介面定義了resolveConstantDesc方法,Constable介面定義了describeConstable方法;String、Integer、Long、Float、Double均實現了這兩個介面,而EnumDesc實現了ConstantDesc介面

64-bit Arm platform (arm64),也可以稱之為aarch64;之前JDK有兩個關於aarch64的實現,分別是src/hotspot/cpu/arm以及open/src/hotspot/cpu/aarch64,它們的實現重複了,為了集中精力更好地實現aarch64,該特性在原始碼中刪除了open/src/hotspot/cpu/arm中關於64-bit的實現,保留其中32-bit的實現,於是open/src/hotspot/cpu/aarch64部分就成了64-bit ARM architecture的預設實現

java10的新特性JEP 310: Application Class-Data Sharing擴充套件了JDK5引入的Class-Data Sharing,支援application的Class-Data Sharing;Class-Data Sharing可以用於多個JVM共享class,提升啟動速度,最早只支援system classes及serial GC,JDK9對其進行擴充套件以支援application classes及其他GC演算法,並在JDK10中開源出來(以前是commercial feature);JDK11將-Xshare:off改為預設-Xshare:auto,以更加方便使用CDS特性;JDK12的這個特性即在64-bit平臺上編譯jdk的時候就預設在${JAVA_HOME}/lib/server目錄下生成一份名為classes.jsa的預設archive檔案(大概有18M)方便大家使用

G1在garbage collection的時候,一旦確定了collection set(CSet)開始垃圾收集這個過程是without stopping的,當collection set過大的時候,此時的STW時間會過長超出目標pause time,這種情況在mixed collections時候比較明顯。這個特性啟動了一個機制,當選擇了一個比較大的collection set,允許將其分為mandatory及optional兩部分(當完成mandatory的部分,如果還有剩餘時間則會去處理optional部分)來將mixed collections從without stopping變為abortable,以更好滿足指定pause time的目標

G1目前只有在full GC或者concurrent cycle的時候才會歸還記憶體,由於這兩個場景都是G1極力避免的,因此在大多數場景下可能不會及時會還committed Java heap memory給作業系統。JDK12的這個特性新增了兩個引數分別是G1PeriodicGCInterval及G1PeriodicGCSystemLoadThreshold,設定為0的話,表示禁用。當上一次garbage collection pause過去G1PeriodicGCInterval(milliseconds)時間之後,如果getloadavg()(one-minute)低於G1PeriodicGCSystemLoadThreshold指定的閾值,則觸發full GC或者concurrent GC(如果開啟G1PeriodicGCInvokesConcurrent),GC之後Java heap size會被重寫調整,然後多餘的記憶體將會歸還給作業系統

細項解讀

上面列出的是大方面的特性,除此之外還有一些api的更新及廢棄,主要見JDK 12 Release Notes,這裡舉幾個例子。

新增項

  • 支援unicode 11
  • 支援Compact Number Formatting

使用例項如下

	@Test
    public void testCompactNumberFormat(){
        var cnf = NumberFormat.getCompactNumberInstance(Locale.CHINA, NumberFormat.Style.SHORT);
        System.out.println(cnf.format(1_0000));
        System.out.println(cnf.format(1_9200));
        System.out.println(cnf.format(1_000_000));
        System.out.println(cnf.format(1L << 30));
        System.out.println(cnf.format(1L << 40));
        System.out.println(cnf.format(1L << 50));
    }
複製程式碼

輸出

1萬
2萬
100萬
11億
1兆
1126兆
複製程式碼
  • String支援transform、indent操作
    @Test
    public void testStringTransform(){
        System.out.println("hello".transform(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return s.hashCode();
            }
        }));
    }

    @Test
    public void testStringIndent(){
        System.out.println("hello".indent(3));
    }
複製程式碼
  • Files新增mismatch方法
    @Test
    public void testFilesMismatch() throws IOException {
        FileWriter fileWriter = new FileWriter("/tmp/a.txt");
        fileWriter.write("a");
        fileWriter.write("b");
        fileWriter.write("c");
        fileWriter.close();

        FileWriter fileWriterB = new FileWriter("/tmp/b.txt");
        fileWriterB.write("a");
        fileWriterB.write("1");
        fileWriterB.write("c");
        fileWriterB.close();

        System.out.println(Files.mismatch(Path.of("/tmp/a.txt"),Path.of("/tmp/b.txt")));
    }
複製程式碼
  • Collectors新增teeing方法用於聚合兩個downstream的結果
    @Test
    public void testCollectorTeeing(){
        var result = Stream.of("Devoxx","Voxxed Days","Code One","Basel One")
                .collect(Collectors.teeing(Collectors.filtering(n -> n.contains("xx"),Collectors.toList()),
                                            Collectors.filtering(n -> n.endsWith("One"),Collectors.toList()),
                        (List<String> list1, List<String> list2) -> List.of(list1,list2)
                                            ));

        System.out.println(result.get(0));
        System.out.println(result.get(1));
    }
複製程式碼
  • CompletionStage新增exceptionallyAsync、exceptionallyCompose、exceptionallyComposeAsync方法
    @Test
    public void testExceptionallyAsync() throws ExecutionException, InterruptedException {
        LOGGER.info("begin");
        int result = CompletableFuture.supplyAsync(() -> {
            LOGGER.info("calculate");
            int i = 1/0;
            return 100;
        }).exceptionallyAsync((t) -> {
            LOGGER.info("error error:{}",t.getMessage());
            return 0;
        }).get();

        LOGGER.info("result:{}",result);
    }
複製程式碼
  • JDK12之前CompletionStage只有一個exceptionally,該方法體在主執行緒執行,JDK12新增了exceptionallyAsync、exceptionallyComposeAsync方法允許方法體在非同步執行緒執行,同時新增了exceptionallyCompose方法支援在exceptionally的時候構建新的CompletionStage

  • Allocation of Old Generation of Java Heap on Alternate Memory Devices

G1及Parallel GC引入experimental特性,允許將old generation分配在諸如NV-DIMM memory的alternative memory device

  • ZGC: Concurrent Class Unloading

ZGC在JDK11的時候還不支援class unloading,JDK12對ZGC支援了Concurrent Class Unloading,預設是開啟,使用-XX:-ClassUnloading可以禁用

  • 新增-XX:+ExtensiveErrorReports

-XX:+ExtensiveErrorReports可以用於在jvm crash的時候收集更多的報告資訊到hs_err.log檔案中,product builds中預設是關閉的,要開啟的話,需要自己新增-XX:+ExtensiveErrorReports引數

  • 新增安全相關的改進

支援java.security.manager系統屬性,當設定為disallow的時候,則不使用SecurityManager以提升效能,如果此時呼叫System.setSecurityManager則會丟擲UnsupportedOperationException keytool新增-groupname選項允許在生成key pair的時候指定一個named group 新增PKCS12 KeyStore配置屬性用於自定義PKCS12 keystores的生成 Java Flight Recorder新增了security-related的event 支援ChaCha20 and Poly1305 TLS Cipher Suites

  • jdeps Reports Transitive Dependences

jdeps的--print-module-deps, --list-deps, 以及--list-reduce-deps選項得到增強,新增--no-recursive用於non-transitive的依賴分析,--ignore-missing-deps用於suppress missing dependence errors

移除項

  • 移除com.sun.awt.SecurityWarnin
  • 移除FileInputStream、FileOutputStream、Java.util.ZipFile/Inflator/Deflator的finalize方法
  • 移除GTE CyberTrust Global Root
  • 移除javac的-source, -target對6及1.6的支援,同時移除--release選項

廢棄項

  • 廢棄的API列表見deprecated-list
  • 廢棄-XX:+/-MonitorInUseLists選項
  • 廢棄Default Keytool的-keyalg值

已知問題

  • Swing不支援GTK+ 3.20及以後的版本
  • 在使用JVMCI Compiler(比如Graal)的時候,JVMTI的can_pop_frame及can_force_early_return的capabilities是被禁用的

其他事項

  • 如果使用者沒有指定user.timezone且從作業系統獲取的為空,那麼user.timezone屬性的初始值為空變為null
  • java.net.URLPermission的行為發生輕微變化,以前它會忽略url中的query及fragment部分,這次改動新增query及fragment部分,即scheme : // authority [ / path ]變動為scheme : // authority [ / path ] [ ignored-query-or-fragment ]
  • javax.net.ssl.SSLContext API及Java Security Standard Algorithm Names規範移除了必須實現TLSv1及TLSv1.1的規定

小結

  • java12不是LTS(Long-Term Support)版本(oracle版本才有LTS),oracle對該版本的support週期為6個月。這個版本主要有幾個更新點,一個是語法層更新,一個是API層面的更新,另外主要是GC方面的更新。
  • 語法層面引入了preview版本的Switch Expressions;API層面引入了JVM Constants API,引入CompactNumberFormat,讓NumberFormat支援COMPACTSTYLE,對String、Files、Collectors、CompletionStage等新增方法;GC方面引入了experimental版本的Shenandoah GC,不過oracle build的openjdk沒有enable Shenandoah GC support;另外主要對ZGC及G1 GC進行了改進
  • 其中JDK12對ZGC支援了Concurrent Class Unloading,預設是開啟,使用-XX:-ClassUnloading可以禁用;對於G1 GC則新增支援Abortable Mixed Collections以及Promptly Return Unused Committed Memory特性

doc