欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java11新特性

程序员文章站 2024-03-14 20:20:59
...

Java8新特性 

 Java9新特性


一、简介 

北京时间 20189 26 日,Oracle 官方宣布 Java 11 正式发布。这是 Java 大版本周期变化后的第一个长期支持版本,非常值得关注。从官网即可下载, 最新发布的 Java11 将带来 ZGCHttp Client 等重要特性,一共包含 17 JEPJDK Enhancement ProposalsJDK 增强提案)。

Java11新特性

JDK 11 将是一个 企业不可忽视的版本。从时间节点来看,JDK 11 的发布正好处在 JDK 8 免费更新到期的前夕,同时 JDK 910 也陆续成为历史版本,下面是 Oracle JDK 支持路线图:

Java11新特性

JDK 更新很重要吗?非常重要,在过去的很多年中,Oracle OpenJDK 社区提供了接近免费的午餐,导致人们忽略了其背后的海量工作和价值,这其中包括但不仅仅限于:最新的安全更新,如,安全协议等基础设施的升级和维护,安全漏洞的及时修补,这是 Java 成为企业核心设施的基础之一。大量的新特性、Bug 修复,例如,容器环境支持,GC 等基础领域的增强。很多生产开发中的 Hack,其实升级 JDK 就能解决了。不断改进的 JVM,提供接近零成本的性能优化

JDK 11 是一个长期支持版本(LTS, Long-Term-Support 

对于企业来说,选择 11 将意味着长期的、可靠的、可预测的技术路线图。其中免费的OpenJDK11 确定将得到 OpenJDK 社区的长期支持, LTS 版本将是可以放心选择的版本。

JVM GC 的角度JDK11 引入了两种新的 GC,其中包括也许是划时代意义的 ZGC,虽然其目前还是实验特性,但是从能力上来看,这是 JDK 的一个巨大突破,为特定生产环境的苛刻需求提供了一个可能的选择。例如,对部分企业核心存储等产品,如果能够保证不超过 10ms GC 暂停,可靠性会上一个大的台阶,这是过去我们进行 GC 调优几乎做不到的,是能与不能的问题。 

Java11新特性

对于 G1 GC,相比于 JDK 8,升级到 JDK 11 即可免费享受到:并行的 Full GC,快速的 CardTable 扫描,自适应的堆占用比例调整(IHOP),在并发标记阶段的类型卸载等等。这些都是针对 G1 的不断增强,其中串行 Full GC 等甚至是曾经被广泛诟病的短板,你会发现 GC 配置和调优在 JDK11 中越来越方便。 

云计算时代的监控、诊断和 Profiling 能力,这个是相比 ZGC 更具生产实践意义的特性。 

Java 的应用场景跨度很大,从单机长时间运行的 Java 应用,发展成为分布式、大的单体应用或小的 function、瞬时或长时间运行等,应用场景非常复杂。 

我们用什么工具诊断 Java 应用?  

JDK 11 为我们提供了更加强大的基础能力,主要是两部分: 

JEP 328: Flight Recorder(飞行记录仪)JFR Oracle 刚刚开源的强大特性。 

Java11新特性

 JFR 是一套集成进入 JDKJVM 内部的事件机制框架,通过良好架构和设计的框架,硬件层面的极致优化,生产环境的广泛验证,它可以做到极致的可靠和低开销。在 SPECjbb2015 等基准测试中,JFR 的性能开销最大不超过 1%,所以,工程师可以基本没有心理负担地在大规模分布式的生产系统使用,这意味着,我们既可以随时主动开启 JFR 进行特定诊断,也可以让系统长期运行 JFR,用以在复杂环境中进行“After-the-fact”分析。

 在保证低开销的基础上,JFR 提供的能力可以应用在对锁竞争、阻塞、延迟,JVM GCSafePoint 等领域,进行非常细粒度分析。甚至深入 JIT Compiler 内部,全面把握热点方法、内联、逆优化等等。JFR 提供了标准的 JavaC++ 等扩展 API,可以与各种层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供 All-in-One 解决方案。

 而这一切都是内建在 JDK JVM 内部的,并不需要额外的依赖,开箱即用。

 JEP 331: Low-Overhead Heap Profiling

它来源于 Google 等业界前沿厂商的一线实践,通过获取对象分配细节,为 JDK 补足了对象分配诊断方面的一些短板,工程师可以通过 JVMTI 使用这个能力增强自身的工具。

Java 类库发展的角度来看,JDK 11 最大的进步也是两个方面:

第一, HTTP/2 Client API,新的 HTTP API 提供了对 HTTP/2 等业界前沿标准的支持,精简而又友好的 API 接口,与主流开源 API(如,Apache HttpClient Jetty OkHttp 等)对等甚至更高的性能。与此同时它是 JDK Reactive-Stream 方面的第一个生产实践,广泛使用了 Java Flow API 等,终于让 Java 标准 HTTP 类库在扩展能力等方面,满足了现代互联网的需求。

第二,就是安全类库、标准等方面的大范围升级,其中特别是 JEP 332: Transport Layer Security (TLS) 1.3,除了在安全领域的重要价值,它还是中国安全专家范学雷所领导的 JDK 项目,完全不同于以往的修修补补,是个非常大规模的工程。

 除此之外,JDK 还在逐渐进行瘦身工作,或者偿还 JVMJava 规范等历史欠账,例如

335: Deprecate the Nashorn JavaScript Engine

它进一步明确了 Graal 很有可能将成为 JVM 向前演进的核心选择,Java-on-Java 正在一步步的成为现实。

 


二、官方更新列表

http://openjdk.java.net/projects/jdk/11/ 

JEP 181: Nest-Based Access Control
JEP 309: Dynamic Class-File Constants
JEP 315: Improve Aarch64 Intrinsics
JEP 318: Epsilon: A No-Op Garbage Collector
JEP 320: Remove the Java EE and CORBA Modules
JEP 321: HTTP Client (Standard)
JEP 323: Local-Variable Syntax for Lambda Parameters
JEP 324: Key Agreement with Curve25519 and Curve448
JEP 327: Unicode 10
JEP 328: Flight Recorder
JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms
JEP 330: Launch Single-File Source-Code Programs
JEP 331: Low-Overhead Heap Profiling
JEP 332: Transport Layer Security (TLS) 1.3
JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental)
JEP 335: Deprecate the Nashorn JavaScript Engine
JEP 336: Deprecate the Pack200 Tools and API 


注 : JEP (JDK Enhancement Proposal 特性增强提议)

01.    JShell。(java9开始支持)

用过Python的童鞋都知道,Python 中的读取-求值-打印循环( Read-Evaluation-Print Loop )很方便。它的目的在于以即时结果和反馈的形式。

java9引入了jshell这个交互性工具,让Java也可以像脚本语言一样来运行,可以从控制台启动 jshell ,在 jshell 中直接输入表达式并查看其执行结果。当需要测试一个方法的运行效果,或是快速的对表达式进行求值时,jshell 都非常实用。

除了表达式之外,还可以创建 Java 类和方法。jshell 也有基本的代码完成功能。我们在教人们如何编写 Java 的过程中,不再需要解释 “public static void main(String [] args)” 这句废话。

jshell> int n = 200;
n ==> 200

jshell> n
n ==> 200
jshell> class A{
   ...> private int age = 20;
   ...> public void setAge(int age){
   ...> this.age = age;
   ...> }
   ...> public int getAge(){
   ...> return this.age;
   ...> }
   ...> }
|  已创建 类 A

jshell> A a = new A();
a ==> aaa@qq.com

jshell> a.setAge(60);

jshell> a.getAge();
$6 ==> 60
jshell> /help
|  键入 Java 语言表达式, 语句或声明。
|  或者键入以下命令之一:
|  /list [<名称或 id>|-all|-start]
|       列出您键入的源
|  /edit <名称或 id>
|       编辑源条目
|  /drop <名称或 id>
|       删除源条目
|  /save [-all|-history|-start] <文件>
|       将片段源保存到文件
|  /open <file>
|       打开文件作为源输入
|  /vars [<名称或 id>|-all|-start]
|       列出已声明变量及其值
|  /methods [<名称或 id>|-all|-start]
|       列出已声明方法及其签名
|  /types [<名称或 id>|-all|-start]
|       列出类型声明
|  /imports
|       列出导入的项
|  /exit [<integer-expression-snippet>]
|       退出 jshell 工具
|  /env [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>] ...
|       查看或更改评估上下文
|  /reset [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>]...
|       重置 jshell 工具
|  /reload [-restore] [-quiet] [-class-path <路径>] [-module-path <路径>]...
|       重置和重放相关历史记录 -- 当前历史记录或上一个历史记录 (-restore)
|  /history [-all]
|       您键入的内容的历史记录
|  /help [<command>|<subject>]
|       获取有关使用 jshell 工具的信息
|  /set editor|start|feedback|mode|prompt|truncation|format ...
|       设置配置信息
|  /? [<command>|<subject>]
|       获取有关使用 jshell 工具的信息
|  /!
|       重新运行上一个片段 -- 请参阅 /help rerun
|  /<id>
|       按 ID 或 ID 范围重新运行片段 -- 参见 /help rerun
|  /-<n>
|       重新运行以前的第 n 个片段 -- 请参阅 /help rerun
|
|  有关详细信息, 请键入 '/help', 后跟
|  命令或主题的名称。
|  例如 '/help /list' 或 '/help intro'。主题:
|
|  intro
|       jshell 工具的简介
|  id
|       片段 ID 以及如何使用它们的说明
|  shortcuts
|       片段和命令输入提示, 信息访问以及
|       自动代码生成的按键说明
|  context
|       /env /reload 和 /reset 的评估上下文选项的说明
|  rerun
|       重新评估以前输入片段的方法的说明

jshell>

 


02.    Dynamic Class-File Constants类文件新添的一种结构

Java的类型文件格式将被拓展,支持一种新的常量池格式:CONSTANT_Dynamic,加载CONSTANT_Dynamic会将创建委托给bootstrap方法。

目标

其目标是降低开发新形式的可实现类文件约束带来的成本和干扰。

 


03.    局部变量类型推断(var关键字)。(java10 开始支持)

 什么是局部变量类型推断?

var javastack = "javastack";
System.out.println(javastack);

大家看出来了,局部变量类型推断就是左边的类型直接使用 var 定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型,如上面的 String 。

var javastack = "javastack";

就等于:

String javastack = "javastack";

在声明隐式类型的lambda表达式的形参时允许使用var,使用var的好处是在使用lambda表达式时给参数加上注解

(@Deprecated var x, @Nullable var y) -> x.process(y);
有参数的lambda表达式使用
函数式接口 : 
	Consumer<T> : 消费型函数式接口.
		public void accept(T t);
		
	Consumer<String> consumer = t -> System.out.println(t.toUpperCase());
	
	Consumer<String> consumer = (var t) -> System.out.println(t.toUpperCase());
	
	错误的形式: 必须要有类型, 可以加上var
	Consumer<String> consumer = (@Deprecated t) -> System.out.println(t.toUpperCase());
	正确的形式:
	Consumer<String> consumer = (@Deprecated var t) -> System.out.println(t.toUpperCase());
	

jshell> Consumer<String> consumer = (@Deprecated var t) -> System.out.println(t.toUp perCase());
consumer ==> $Lambda$15/aaa@qq.com

jshell> Consumer<String> consumer = (@Deprecated t) -> System.out.println(t.toUpperCase());
|  错误:
|  非法的表达式开始
|  Consumer<String> consumer = (@Deprecated t) -> System.out.println(t.toUpperCase());
|                               ^---------^
|  错误:
|  需要')'
|  Consumer<String> consumer = (@Deprecated t) -> System.out.println(t.toUpperCase());
|                                          ^
|  错误:
|  需要';'
|  Consumer<String> consumer = (@Deprecated t) -> System.out.println(t.toUpperCase());
|                                            ^

//我上面我要标记一个变量已过期,此时需要一个变量的类型,但是我有不关心这个变量具体是什么类型,这个时候就一个用 var 来代替

jshell> Thread t = new Thread(()->System.out.println(Thread.currentThread().getName()));
t ==> Thread[Thread-0,5,main]

jshell> t.start();

jshell> Thread-0

jshell> Consumer<String> consumer = t -> System.out.println(t.toUpperCase());
consumer ==> $Lambda$22/aaa@qq.com

jshell> consumer.accept("abcABC");
ABCABC

 

注意:

jshell> var n;
|  错误:
|  无法推断本地变量 n 的类型
|    (无法在不带初始化程序的变量上使用 'var')
|  var n;
|  ^----^

jshell> class B{
   ...> var id;
   ...> }
|  错误:
|  此处不允许使用 'var'
|  var id;
|  ^-^

jshell>

var 语法 : 局部变量的类型推断.
注意点 : 
    1) var a; 这样不可以, 因为无法推断.
    2) 类的属性的数据类型不可以使用var. 


04.    新加的一些更实用的API

1.  新的本机不可修改集合API

自 Java 9 开始,Jdk 里面为集合(List/ Set/ Map)都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。

示例1:

var list = List.of("Java", "Python", "C");
var copy = List.copyOf(list);
System.out.println(list == copy); // true

示例2:

var list = new ArrayList<String>();
var copy = List.copyOf(list);
System.out.println(list == copy); // false

示例1和2代码差不多,为什么一个为true,一个为false?

来看下它们的源码:

static <E> List<E> of(E... elements) {
  switch (elements.length) { // implicit null check of elements
    case 0:
        return ImmutableCollections.emptyList();
    case 1:
        return new ImmutableCollections.List12<>(elements[0]);
    case 2:
        return new ImmutableCollections.List12<>(elements[0], elements[1]);
    default:
        return new ImmutableCollections.ListN<>(elements);
  }
}

static <E> List<E> copyOf(Collection<? extends E> coll) {
    return ImmutableCollections.listCopy(coll);
}
static <E> List<E> listCopy(Collection<? extends E> coll) {
    if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {
        return (List<E>)coll;
    } else {
        return (List<E>)List.of(coll.toArray());
    }
}

可以看出 copyOf 方法会先判断来源集合是不是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。

示例2因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为false.

注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有。

除了更短和更好阅读之外,这些方法也可以避免您选择特定的集合实现。在创建后,继续添加元素到这些集合会导致 “UnsupportedOperationException” 。

 

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.Test;
public class APITest {
	// 集合中的一些增强的API
//of 方法的扩展 of 方向一般都是用于创建对象,根据给定的参数来创建对象
	@Test
	public void test3() {
		LocalDate localDate = LocalDate.of(2019, 1, 21);//java8  的新方法
		// 在添加重复元素时, 不是无法添加, 而是抛出异常
		//Set<Integer> set = Set.of(100, 50, 20, 30, 10, 8, 100, 8);
		Set<Integer> set = Set.of(100, 50, 20, 30, 10, 8);
		System.out.println(set.getClass());
		
		Stream<Integer> stream = Stream.of(50, 20, 30, 70);
	}
	
	@Test
	public void test2() {
		int[] arr = {1, 9, 3, 2, 8};
		// 快速把数据变成集合的方法
		List<String> list1 = Arrays.asList("aa", "yyy", "zzz", "123");
		//list1.add("ppp"); // 是一个不可以添加元素的集合
		list1.forEach(System.out::println);

		// 集合的创建可以使用更简单的方式
		List<String> list2 = List.of("aa", "bbb", "cc", "DD");
		list2.forEach(System.out::println);
		//list2.add("yyy"); // 不可以添加元素
	}
	
	@Test
	public void test1() {
		List<String> list = new ArrayList<>();
		list.add("aa");
		list.add("bbb");
		list.add("cc");
		list.add("DD");
		list.forEach(System.out::println);
	}
}

2.  Stream 加强

Stream 是 Java 8 中的新特性,Java 9 开始对 Stream 增加了以下 4 个新方法。

1) 增加单个参数构造方法,可为null

Stream.ofNullable(null).count(); // 0

2) 增加 takeWhile 和 dropWhile 方法

Stream.of(1, 2, 3, 2, 1)
.takeWhile(n -> n < 3)
.collect(Collectors.toList()); // [1, 2]

从开始计算,当 n < 3 时就截止。

Stream.of(1, 2, 3, 2, 1)
.dropWhile(n -> n < 3)
.collect(Collectors.toList()); // [3, 2, 1]

这个和上面的相反,一旦 n < 3 不成立就开始计算。

3)iterate重载

这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。

流的操作: 

import java.util.stream.Stream;
import org.junit.Test;
/**
 * 流的处理
 * 1) 创建流
 * 2) 中间操作
 * 3) 终止操作
 */

public class StreamTest {
	
	@Test
	public void test3() {
		// 流的迭代, 创建流
		Stream<Integer> stream1 = Stream.iterate(1, t -> (2 * t) + 1);
		stream1.limit(10).forEach(System.out::println);
		
		System.out.println("*************************");
		// 有限的迭代
		Stream<Integer> stream2 = Stream.iterate(1, t -> t < 1000, t -> (2 * t) + 1);
		stream2.forEach(System.out::println);
	}
	
	@Test
	public void test2() {
		Stream<Integer> stream1 = Stream.of(3, 9, 20, 22, 40, 7);
		// 新方法, takeWhile, dropWhile
		// 从流中一直获取判定器为真的元素, 一旦遇到元素为假, 就终止处理.
		Stream<Integer> stream2 = stream1.takeWhile(t -> t % 2 != 0);
		stream2.forEach(System.out::println);
		
		System.out.println("**********************");
		stream1 = Stream.of(3, 9, 20, 22, 40, 7);
		Stream<Integer> stream3 = stream1.dropWhile(t -> t % 2 != 0);
		stream3.forEach(System.out::println);
		
	}
	
	@Test
	public void test1() {
		Stream<Integer> stream1 = Stream.of(3, 9, 20, 22, 40);
		//stream1.forEach(t -> System.out.println(t));
		stream1.forEach(System.out::println);
		
		System.out.println("***********************************");
		
		Stream<Object> stream2 = Stream.of(); // 流中没有数据
		stream2.forEach(System.out::println);
		
		System.out.println("***********************************");
		
		// 传入null会被解析为是一个数组对象, 会进一步访问它的长度信息
		//Stream<Object> stream3 = Stream.of(null);
		//stream3.forEach(System.out::println);
		
		// 可以传入 一个null来创建流对象
		Stream<Object> stream3 = Stream.ofNullable(null);
		stream3.forEach(System.out::println);
	}
}

Stream.of(null) 不能为null

Java11新特性

Java11新特性

Arrays.asList() 不可以添加元素源码分析:

Java11新特性

Java11新特性

Java11新特性

Java11新特性
List.of() 不可以添加元素源码分析:

Java11新特性

Java11新特性

Java11新特性

Java11新特性

 

3.  增加了一系列的字符串处理方法

如以下所示。

// 判断字符串是否为空白
" ".isBlank(); // true
// 去除首尾空白
" Javastack ".strip(); // "Javastack"
// 去除尾部空格
" Javastack ".stripTrailing(); // " Javastack"
// 去除首部空格
" Javastack ".stripLeading(); // "Javastack "
// 复制字符串
"Java".repeat(3);// "JavaJavaJava"
// 行数统计
"A\nB\nC".lines().count(); // 3
@Test
public void testName() throws Exception {
	String string = " \t  \r\n ";
	System.out.println(string.isBlank()); // 判断字符串中的字符是否都是空白
	
	System.out.println("**************************");
	
	string = " \t  \r\n abc \t ";//汉字的空格
	String string2 = string.strip(); // 去重字符串首尾的空白, 包括英文和其他所有语言中的空白字符
	System.out.println(string2);
	System.out.println(string2.length());
	
	String string3 = string.trim(); // 去重字符串首尾的空白字符, 只能去除码值小于等于32的空白字符
	System.out.println(string3);
	System.out.println(string3.length());//6 汉字的 空格+tab+汉字的空格
	
	System.out.println("**************************");
	String string4 = string.stripLeading(); // 去重字符串首部的空白
	System.out.println(string4);
	System.out.println(string4.length());
	
	String string5 = string.stripTrailing(); // 去重字符串尾部的空白
	System.out.println(string5);
	System.out.println(string5.length());
}
@Test
public void testName2() throws Exception {
	String string = "Java";
	String string2 = string.repeat(5);
	System.out.println(string2);

	Stream<String> lines = "A\nB\nC".lines();//返回的是流 而 split是字符数组
}
@Test
public void testName3() throws Exception {
	FileInputStream fis = new FileInputStream("src/com/atguigu/java11/StringTest.java");
	byte[] buffer = new byte[fis.available()];
	fis.read(buffer);
	fis.close();
	String string = new String(buffer);//把读入的内容变成一个 大字符串
	string.lines().forEach(System.out::println);//把字符串以换行符为分割 分割的字符串形成一个流
}

 

 4.  Optional 加强

Opthonal 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。

Optional.of("javastack").orElseThrow(); // javastack
Optional.of("javastack").stream().count(); // 1
Optional.ofNullable(null)
.or(() -> Optional.of("javastack"))
.get(); // javastack
@Test
public void testName() throws Exception {
	// of方法中如果传入的参数是null, 会抛出空指针异常
	//Optional<String> optional = Optional.of(null);
	// ofNullable可以兼容空指针, 但是实际传入null后要小心
	Optional<Object> optional = Optional.ofNullable(null);
	Object object = optional.orElse("abc"); // 如果内部引用为空, 则返回参数中的引用, 否则返回内部引用
	System.out.println(object);
	
	Object object2 = optional.orElseThrow();
	System.out.println(object2);
}
    /**
     * If a value is present, returns the value, otherwise throws
     * {@code NoSuchElementException}.
     *
     * @return the non-{@code null} value described by this {@code Optional}
     * @throws NoSuchElementException if no value is present
     * @since 10
     */
    public T orElseThrow() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
    /**
     * If a value is present, returns the value, otherwise returns the result
     * produced by the supplying function.
     *
     * @param supplier the supplying function that produces a value to be returned
     * @return the value, if present, otherwise the result produced by the
     *         supplying function
     * @throws NullPointerException if no value is present and the supplying
     *         function is {@code null}
     */
    public T orElseGet(Supplier<? extends T> supplier) {
        return value != null ? value : supplier.get();
    }

 

 5.  改进的文件API。

 InputStream 加强

InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例。

var classLoader = ClassLoader.getSystemClassLoader();
var inputStream = classLoader.getResourceAsStream("javastack.txt");
var javastack = File.createTempFile("javastack2", "txt");
try (var outputStream = new FileOutputStream(javastack)) {
    inputStream.transferTo(outputStream);
}
@Test
public void testName() throws Exception {
	var cl = this.getClass().getClassLoader();//类加载器  它可以直接读 src 下面的内容
	try (var is = cl.getResourceAsStream("file"); var os = new FileOutputStream("file2")) {
		is.transferTo(os); // 把输入流中的所有数据直接自动地复制到输出流中  不需要循环读取,也不需要缓冲区
	}
}
//记得要再 src 下创建一个 scr文件

 


05.    移除的一些其他内容

 移除项

  1. 移除了com.sun.awt.AWTUtilities
  2. 移除了sun.misc.Unsafe.defineClass,
  3. 使用java.lang.invoke.MethodHandles.Lookup.defineClass来替代
  4. 移除了Thread.destroy()以及 Thread.stop(Throwable)方法
  5. 移除了sun.nio.ch.disableSystemWideOverlappingFileLockCheck、sun.locale.formatasdefault属性
  6. 移除了jdk.snmp模块
  7. 移除了javafx,openjdk估计是从java10版本就移除了,oracle jdk10还尚未移除javafx,而java11版本则oracle的jdk版本也移除了javafx
  8. 移除了Java Mission Control,从JDK中移除之后,需要自己单独下载
  9. 移除了这些Root Certificates :Baltimore Cybertrust Code Signing CA,SECOM ,AOL and Swisscom

废弃项

  1. -XX+AggressiveOpts选项
  2. -XX:+UnlockCommercialFeatures   (解锁商业插件)
  3. -XX:+LogCommercialFeatures选项也不再需要
     

06.    标准Java异步HTTP客户端。

这是 Java 9 开始引入的一个处理 HTTP 请求的的 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在 java.net 包中找到这个 API。

来看一下 HTTP Client 的用法:

var request = HttpRequest.newBuilder()
.uri(URI.create("https://javastack.cn"))
.GET()
.build();
var client = HttpClient.newHttpClient();
// 同步
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
// 异步
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);

上面的 .GET() 可以省略,默认请求方式为 Get!

更多使用示例可以看这个 API,后续有机会再做演示。

现在 Java 自带了这个 HTTP Client API,我们以后还有必要用 Apache 的 HttpClient 工具包吗?

//同步的
@Test
public void testName() throws Exception {
	HttpClient client = HttpClient.newHttpClient();
	HttpRequest request = HttpRequest.newBuilder(URI.create("https://blog.csdn.net/qq_40794973/article/details/87338359")).build();
	BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();
	HttpResponse<String> response = client.send(request, responseBodyHandler);//阻塞 返回结果才继续往下执行
	String body = response.body();
	System.out.println(body);
}
//异步
@Test
public void testName2() throws Exception {
	HttpClient client = HttpClient.newHttpClient();
	HttpRequest request = HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
	BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();
	CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, responseBodyHandler);//不会阻塞 马上就会去执行下面的代码
	//Thread.sleep(2000);//没结果 sleep 试一试
	sendAsync.thenApply(t -> t.body()).thenAccept(System.out::println);
	//HttpResponse<String> response = sendAsync.get();
	//String body = response.body();
	//System.out.println(body);
	
}


07.    更简化的编译运行

 JEP 330 : 增强java启动器支持运行单个java源代码文件的程序.

 注意点 :

  1. 执行源文件中的第一个类, 第一个类必须包含主方法
  2. 并且不可以使用别源文件中的自定义类, 本文件中的自定义类是可以使用的.

一个命令编译运行源代码

看下面的代码。

// 编译
javac Javastack.java
// 运行
java Javastack

 在我们的认知里面,要运行一个 Java 源代码必须先编译,再运行,两步执行动作。而在未来的 Java 11 版本中,通过一个 java 命令就直接搞定了,如以下所示。

 java Javastack.java

 

演示:

 创建文件 HelloJava .java

public class HelloJava {
	public static void main(String[] args) {
		System.out.println("Hello Java Simple");
		Teacher t = new Teacher();
	}
}

 Java11新特性

 修改上面的文件执行:

class Test2 {
	public static void main(String[] args) {
		System.out.println("Test2. main");
	}
}
public class HelloJava {
	public static void main(String[] args) {
		System.out.println("Hello Java Simple");
	}
}

Java11新特性

 结论:从上到下一以第一个类为主类,执行主类中的主方法,如果主类中没有主方法会报错的

Java11新特性

Java11新特性

 创建一个 Teacher 类

public class Teacher {
	private String name;
	private int age;
}
public class HelloJava {
	public static void main(String[] args) {
		System.out.println("Hello Java Simple");
		Teacher t = new Teacher();
	}
}
class Test2 {
	public static void main(String[] args) {
		System.out.println("Test2. main");
	}
}

 

 Java11新特性

 把 Teacher  放入一个文件

public class HelloJava {
	public static void main(String[] args) {
		System.out.println("Hello Java Simple");
		Teacher t = new Teacher();
	}
}
class Test2 {
	public static void main(String[] args) {
		System.out.println("Test2. main");
	}
}
public class Teacher {
	private String name;
	private int age;
}

Java11新特性

 在一个源文件中的其他类是可以访问的,跨文件的是不可以的,文件可以有多个 public 类,文件名字随便取。 

 


08.    Unicode 10

Unicode 10 增加了8518个字符, 总计达到了136690个字符. 并且增加了4个脚本.同时还有56个新的emoji表情符号.

136690个字符超过了 char 的范围 

Java11新特性 原来用两个字节,现在把它们扩展为四个字节来处理

如果处理包含了很大码值的字符,用 String 的方法即可

 列举:

    /**
     * Returns the {@code char} value at the
     * specified index. An index ranges from {@code 0} to
     * {@code length() - 1}. The first {@code char} value of the sequence
     * is at index {@code 0}, the next at index {@code 1},
     * and so on, as for array indexing.
     *
     * <p>If the {@code char} value specified by the index is a
     * <a href="Character.html#unicode">surrogate</a>, the surrogate
     * value is returned.
     *
     * @param      index   the index of the {@code char} value.
     * @return     the {@code char} value at the specified index of this string.
     *             The first {@code char} value is at index {@code 0}.
     * @exception  IndexOutOfBoundsException  if the {@code index}
     *             argument is negative or not less than the length of this
     *             string.
     */
    public char charAt(int index) {
        if (isLatin1()) {
            return StringLatin1.charAt(value, index);
        } else {
            return StringUTF16.charAt(value, index);
        }
    }

    /**
     * Returns the character (Unicode code point) at the specified
     * index. The index refers to {@code char} values
     * (Unicode code units) and ranges from {@code 0} to
     * {@link #length()}{@code  - 1}.
     *
     * <p> If the {@code char} value specified at the given index
     * is in the high-surrogate range, the following index is less
     * than the length of this {@code String}, and the
     * {@code char} value at the following index is in the
     * low-surrogate range, then the supplementary code point
     * corresponding to this surrogate pair is returned. Otherwise,
     * the {@code char} value at the given index is returned.
     *
     * @param      index the index to the {@code char} values
     * @return     the code point value of the character at the
     *             {@code index}
     * @exception  IndexOutOfBoundsException  if the {@code index}
     *             argument is negative or not less than the length of this
     *             string.
     * @since      1.5
     */
    public int codePointAt(int index) { //返回 int 形的码值
        if (isLatin1()) {
            checkIndex(index, value.length);
            return value[index] & 0xff;
        }
        int length = value.length >> 1;
        checkIndex(index, length);
        return StringUTF16.codePointAt(value, index, length);
    }

    public int codePointAt(int index) {
        if (isLatin1()) {
            checkIndex(index, value.length);
            return value[index] & 0xff;
        }
        int length = value.length >> 1;
        checkIndex(index, length);
        return StringUTF16.codePointAt(value, index, length);
    }

方法中有 codePoint 单词的都是和扩展的  Unicode  是相关的



09.    Remove the JavaEE and CORBA Moudles

在java11中移除了不太使用的JavaEE模块和CORBA技术

CORBA来自于二十世纪九十年代,Oracle说,现在用CORBA开发现代Java应用程序已经没有意义了,维护CORBA的成本已经超过了保留它带来的好处。

但是删除CORBA将使得那些依赖于JDK提供部分CORBA API的CORBA实现无法运行。目前还没有第三方CORBA版本,也不确定是否会有第三方愿意接手CORBA API的维护工作。

在java11中将java9标记废弃的Java EE及CORBA模块移除掉,具体如下:

 (1)xml 相关的

  • java.xml.ws, 
  • java.xml.bind,
  • java.xml.ws,
  • java.xml.ws.annotation,
  • jdk.xml.bind,
  • jdk.xml.ws被移除,

只剩下java.xml,java.xml.crypto,jdk.xml.dom这几个模块;

(2)java.corba

  • java.se.ee,
  • java.activation,
  • java.transaction被移除,

但是java11新增一个java.transaction.xa模块



10.    JEP : 335 : Deprecate the Nashorn JavaScript Engine

 废除Nashorn javascript引擎,在后续版本准备移除掉,有需要的可以考虑使用GraalVM



11.    JEP : 336 : Deprecate the Pack200 Tools and API

Java5中带了一个压缩工具:Pack200,这个工具能对普通的jar文件进行高效压缩。其  实现原理是根据Java类特有的结构,合并常数  池,去掉无用信息等来实现对java类的高效压缩。由于是专门对Java类进行压缩的,所以对普通文件的压缩和普通压缩软件没有什么两样,但是对于Jar  文件却能轻易达到10-40%的压缩率。这在Java应用部署中很有用,尤其对于移动Java计算,能够大大减小代码下载量。

Java5中还提供了这一技术的API接口,你可以将其嵌入到你的程序中使用。使用的方法很简单,下面的短短几行代码即可以实现jar的压缩和解压:

压缩

Packer packer=Pack200.newPacker(); 
OutputStream output=new BufferedOutputStream(new  FileOutputStream(outfile)); 
packer.pack(new JarFile(jarFile), output); 
output.close(); 

 解压

Unpacker unpacker=Pack200.newUnpacker(); 
output=new JarOutputStream(new FileOutputStream(jarFile)); 
unpacker.unpack(pack200File, output); 
output.close(); 

Pack200的压缩和解压缩速度是比较快的,而且压缩率也是很惊人的,在我是使用  的包4.46MB压缩后成了1.44MB(0.322%),而且随着包的越大压缩率会根据明显,据说如果jar包都是class类可以压缩到1/9的大  小。其实JavaWebStart还有很多功能,例如可以按不同的jar包进行lazy下载和 单独更新,设置可以根据jar中的类变动进行class粒度的下载。

但是在java11中废除了pack200以及unpack200工具以及java.util.jar中的Pack200 API。因为Pack200主要是用来压缩jar包的工具,由于网络下载速度的提升以及java9引入模块化系统之后不再依赖Pack200,因此这个版本将其移除掉。

 



12.    新的Epsilon垃圾收集器。

A NoOp Garbage Collector (不做任何操作的垃圾回收器)

JDK上对这个特性的描述是: 开发一个处理内存分配但不实现任何实际内存回收机制的GC, 一旦可用堆内存用完, JVM就会退出.

如果有System.gc()调用, 实际上什么也不会发生(这种场景下和-XX:+DisableExplicitGC效果一样), 因为没有内存回收, 这个实现可能会警告用户尝试强制GC是徒劳.

用法 : -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC

class Garbage {
	int n = (int)(Math.random() * 100);
	@Override
	public void finalize() {
		System.out.println(this + " : " + n + " is dying");
	}
}
public class EpsilonTest {
	
	public static void main(String[] args) {
		boolean flag = true;
		List<Garbage> list = new ArrayList<>();
		long count = 0;
		while (flag) {
			list.add(new Garbage());
			if (list.size() == 1000000 && count == 0) {
				list.clear();
				count++;
			}
		}
		System.out.println("程序结束");
	}
}

演示: 

import java.util.ArrayList;
import java.util.List;
class Garbage {
	private double d1 = 1;
	private double d2 = 2;
	// 这个方法是GC在清除本对象时, 会调用一次
	@Override
	public void finalize() {
		System.out.println(this + " collecting");
	}
}

public class EpsilonTest {
	// 执行: -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
	public static void main(String[] args) {
		List<Garbage> list = new ArrayList<>();
		boolean flag = true;
		int count = 0;
		while (flag) {
			list.add(new Garbage());
			if (count++ == 500) {
				list.clear();
			}
		}
	}
}

 

 Java11新特性

 Java11新特性

Java11新特性

 如果使用选项-XX:+UseEpsilonGC, 程序很快就因为堆空间不足而退出

使用这个选项的原因 :

  • 提供完全被动的GC实现, 具有有限的分配限制和尽可能低的延迟开销,但代价是内存占用和内存吞吐量.
  • 众所周知, java实现可广泛选择高度可配置的GC实现. 各种可用的收集器最终满足不同的需求, 即使它们的可配置性使它们的功能相交. 有时更容易维护单独的实现, 而不是在现有GC实现上堆积另一个配置选项.

主要用途如下 :

  • 性能测试(它可以帮助过滤掉GC引起的性能假象)
  • 内存压力测试(例如,知道测试用例 应该分配不超过1GB的内存, 我们可以使用-Xmx1g –XX:+UseEpsilonGC, 如果程序有问题(内存泄漏, 则程序会崩溃)
  • 非常短的JOB任务(对象这种任务, 接受GC清理堆那都是浪费空间)
  • VM接口测试
  • Last-drop 延迟&吞吐改进


13.    新的ZGC垃圾收集器 

ZGC, A Scalable Low-Latency Garbage Collector(Experimental)

ZGC, 这应该是JDK11最为瞩目的特性, 没有之一. 但是后面带了Experimental, 说明这还不建议用到生产环境.

  • GC暂停时间不会超过10ms
  • 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)
  • 和G1相比, 应用吞吐能力不会下降超过15%
  • 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础
  • 初始只支持64位系统(32位的 jdk 只能用到 4 个G,和ZGC的设计目标不符合

ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15% 将来还可以扩展实现机制,以支持不少令人兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存),或压缩堆。

GC是java主要优势之一. 然而, GC停顿太长, 就会开始影响应用的响应时间.消除或者减少GC停顿时长, java将对更广泛的应用场景是一个更有吸引力的平台. 此外, 现代系统中可用内存不断增长,用户和程序员希望JVM能够以高效的方式充分利用这些内存, 并且无需长时间的GC暂停时间.

STW – stop the world

ZGC是一个并发, 基于region, 压缩型的垃圾收集器, 只有root扫描阶段会STW, 因此GC停顿时间不会随着堆的增长和存活对象的增长而变长.

ZGC : avg 1.091ms  max:1.681
G1 : avg 156.806  max:543.846

用法 : -XX:+UnlockExperimentalVMOptions –XX:+UseZGC, 因为ZGC还处于实验阶段, 所以需要通过JVM参数来解锁这个特性

注意:当前 Windows 平台还不支持,可以在 Linux 系统上测试。

import java.util.ArrayList;
import java.util.List;
class Garbage {
	private double d1 = 1;
	private double d2 = 2;
	// 这个方法是GC在清除本对象时, 会调用一次
	@Override
	public void finalize() {
		System.out.println(this + " collecting");
	}
}

public class ZGCTest {
	
	// 执行: -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
	public static void main(String[] args) {
		List<Garbage> list = new ArrayList<>();
		boolean flag = true;
		int count = 0;
		while (flag) {
			list.add(new Garbage());
			if (count++ % 500 == 0) {
				list.clear();
			}
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 javac ZGCTest.java

java -Xmx100m -XX:+UnlockExperimentalVMOptions –XX:+UseEpsilonGC ZGCTest

java -XX:+UnlockExperimentalVMOptions –XX:+UseZGC  ZGCTest

-Xmx100m 限制运行时堆的大小 最大值为100M

一文读懂Java 11的ZGC为何如此高效 



14.    完全支持Linux容器(包括Docker)。

许多运行在Java虚拟机中的应用程序(包括Apache Spark和Kafka等数据服务以及传统的企业应用程序)都可以在Docker容器中运行。但是在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行JVM程序在设置内存大小和CPU使用率后,会导致应用程序的性能下降。这是因为Java应用程序没有意识到它正在容器中运行。随着Java 10的发布,这个问题总算得以解决,JVM现在可以识别由容器控制组(cgroups)设置的约束。可以在容器中使用内存和CPU约束来直接管理Java应用程序,其中包括:

        遵守容器中设置的内存限制
        在容器中设置可用的CPU
        在容器中设置CPU约束
Java 10的这个改进在Docker for Mac、Docker for Windows以及Docker Enterprise Edition等环境均有效。

容器的内存限制

在Java 9之前,JVM无法识别容器使用标志设置的内存限制和CPU限制。而在Java 10中,内存限制会自动被识别并强制执行。

Java将服务器类机定义为具有2个CPU和2GB内存,以及默认堆大小为物理内存的1/4。例如,Docker企业版安装设置为2GB内存和4个CPU的环境,我们可以比较在这个Docker容器上运行Java 8和Java 10的区别。

首先,对于Java 8:

docker container run -it -m512 --entrypoint bash openjdk:latest
$ docker-java-home/bin/java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
uintx MaxHeapSize                              := 524288000                          {product}
openjdk version "1.8.0_162"
1
2
3
4

最大堆大小为512M或Docker EE安装设置的2GB的1/4,而不是容器上设置的512M限制。

相比之下,在Java 10上运行相同的命令表明,容器中设置的内存限制与预期的128M非常接近:

docker container run -it -m512M --entrypoint bash openjdk:10-jdk
$ docker-java-home/bin/java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
size_t MaxHeapSize                              = 134217728                          {product} {ergonomic}
openjdk version "10" 2018-03-20
1
2
3
4

设置可用的CPU

默认情况下,每个容器对主机CPU周期的访问是无限的。可以设置各种约束来限制给定容器对主机CPU周期的访问。Java 10可以识别这些限制:

docker container run -it --cpus 2 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2
1
2
3

分配给Docker EE的所有CPU会获得相同比例的CPU周期。这个比例可以通过修改容器的CPU share权重来调整,而CPU share权重与其它所有运行在容器中的权重相关。此比例仅适用于正在运行的CPU密集型的进程。当某个容器中的任务空闲时,其他容器可以使用余下的CPU时间。实际的CPU时间的数量取决于系统上运行的容器的数量。这些可以在Java 10中设置:

docker container run -it --cpu-shares 2048 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2
1
2
3

cpuset约束设置了哪些CPU允许在Java 10中执行。

docker run -it --cpuset-cpus="1,2,3" openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 3
1
2
3

分配内存和CPU

使用Java 10,可以使用容器设置来估算部署应用程序所需的内存和CPU的分配。我们假设已经确定了容器中运行的每个进程的内存堆和CPU需求,并设置了JAVA_OPTS配置。例如,如果有一个跨10个节点分布的应用程序,其中五个节点每个需要512Mb的内存和1024个CPU-shares,另外五个节点每个需要256Mb和512个CPU-shares。

请注意,1个CPU share比例由1024表示。

对于内存,应用程序至少需要分配5Gb。

512Mb × 5 = 2.56Gb
256Mb × 5 = 1.28Gb

该应用程序需要8个CPU才能高效运行。

1024 x 5 = 5个CPU
512 x 5 = 3个CPU

最佳实践是建议分析应用程序以确定运行在JVM中的每个进程实际需要多少内存和分配多少CPU。但是,Java 10消除了这种猜测,可以通过调整容器大小以防止Java应用程序出现内存不足的错误以及分配足够的CPU来处理工作负载。



15.    支持G1上的并行完全垃圾收集。

对于 G1 GC,相比于 JDK 8,升级到 JDK 11 即可免费享受到:并行的 Full GC,快速的 CardTable 扫描,自适应的堆占用比例调整(IHOP),在并发标记阶段的类型卸载等等。这些都是针对 G1 的不断增强,其中串行 Full GC 等甚至是曾经被广泛诟病的短板,你会发现 GC 配置和调优在 JDK11 中越来越方便。 



16.   免费的低耗能飞行记录仪和堆分析仪。

 JEP 331 : Low-Overhead Heap Profiling

通过 JVMTI 的 SampledObjectAlloc 回调提供了一个开销低的heap分析方式,提供一个低开销的, 为了排错java应用问题, 以及JVM问题的数据收集框架, 希望达到的目标如下 :

       提供用于生产和消费数据作为事件的API
       提供缓存机制和二进制数据格式
       允许事件配置和事件过滤
       提供OS,JVM和JDK库的事件

注释:JVMTI  T tools(工具)l interface (接口)  ,JVMTI  就是 一个系列 java 虚拟机的工具接口。JVMTI  实现,java 不可以。



17.    JEP 329 : 实现ChaCha20和Poly1305两种加密算法

JEP 329 : 实现RFC7539中指定的ChaCha20和Poly1305两种加密算法, 代替RC4

实现 RFC 7539ChaCha20 and ChaCha20-Poly1305加密算法

RFC7748定义的秘钥协商方案更高效, 更安全. JDK增加两个新的接口

XECPublicKey 和 XECPrivateKey
KeyPairGenerator kpg = KeyPairGenerator.getInstance(“XDH”);
NamedParameterSpec paramSpec = new NamedParameterSpec(“X25519”);
kpg.initialize(paramSpec);
KeyPair kp = kgp.generateKeyPair();

KeyFactory kf = KeyFactory.getInstance(“XDH”);
BigInteger u = new BigInteger(“xxx”);
XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);
PublicKey pubKey = kf.generatePublic(pubSpec);

KeyAgreement ka = KeyAgreement.getInstance(“XDH”);
ka.init(kp.getPrivate());
ka.doPhase(pubKey, true);
byte[] secret = ka.generateSecret();

 



18.    新的默认根权限证书集。

安全相关



19.    JEP 332最新的HTTPS安全协议TLS 1.3。

实现TLS协议1.3版本, TLS允许客户端和服务器端通过互联网以一种防止窃听, 篡改以及消息伪造的方式进行通信. 



20.    Java Flight Recorder 飞行记录仪

Flight Recorder源自飞机的黑盒子

Flight Recorder以前是商业版的特性,在java11当中开源出来,它可以导出事件到文件中,之后可以用Java Mission Control来分析。可以在应用启动时配置 java -XX:StartFlightRecording 或者在应用启动之后,使用 jcmd 来录制,比如

$ jcmd <pid> JFR.start
$ jcmd <pid> JFR.dump filename=recording.jfr
$ jcmd <pid> JFR.stop

是 Oracle 刚刚开源的强大特性。我们知道在生产系统进行不同角度的 Profiling,有各种工具、框架,但是能力范围、可靠性、开销等,大都差强人意,要么能力不全面,要么开销太大,甚至不可靠可能导致 Java 应用进程宕机。

JFR 是一套集成进入 JDKJVM 内部的事件机制框架,通过良好架构和设计的框架,硬件层面的极致优化,生产环境的广泛验证,它可以做到极致的可靠和低开销。在 SPECjbb2015 等基准测试中,JFR 的性能开销最大不超过 1%,所以,工程师可以基本没有心理负担地在大规模分布式的生产系统使用,这意味着,我们既可以随时主动开启 JFR 进行特定诊断,也可以让系统长期运行 JFR,用以在复杂环境中进行“After-the-fact”分析。还需要苦恼重现随机问题吗?JFR 让问题简化了很多。

在保证低开销的基础上,JFR 提供的能力也令人眼前一亮,例如:我们无需 BCI 就可以进行 Object Allocation Profiling,终于不用担心 BTrace 之类把进程搞挂了。对锁竞争、阻塞、延迟,JVM GCSafePoint 等领域,进行非常细粒度分析。甚至深入 JIT Compiler 内部,全面把握热点方法、内联、逆优化等等。JFR 提供了标准的 JavaC++ 等扩展 API,可以与各种层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供 All-in-One 解决方案。而这一切都是内建在 JDK JVM 内部的,并不需要额外的依赖,开箱即用。

 

演示:

创建文件 JFRTest .java

import java.util.ArrayList;
import java.util.List;
class Garbage {
	private double d1 = 1;
	private double d2 = 2;
	// 这个方法是GC在清除本对象时, 会调用一次
	@Override
	public void finalize() {
		System.out.println(this + " collecting");
	}
}

public class JFRTest {
	public static void main(String[] args) {
		List<Garbage> list = new ArrayList<>();
		boolean flag = true;
		int count = 0;
		while (flag) {
			list.add(new Garbage());
			if (count++ == 20) {
				list.clear();				
			}
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}	
		}
	}
}

 编译 执行上面的文件

PS D:\> java -version
java version "11" 2018-09-25
Java(TM) SE Runtime Environment 18.9 (build 11+28)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11+28, mixed mode)
PS D:\> javac JFRTest.java
注: JFRTest.java使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
PS D:\> java JFRTest 

 上面的程序会一直等待

在在当前窗口打开一个 命令行

Java11新特性

Java11新特性

D:\>jcmd 1836 JFR.start
1836:
Started recording 1. No limit specified, using maxsize=250MB as default.
Use jcmd 1836 JFR.dump name=1 filename=FILEPATH to copy recording data to file.

D:\>jcmd 1836 JFR.dump filename=test.jfr name=1
1836:
Dumped recording "1", 428.7 kB written to:
D:\test.jfr

D:\>jcmd 1836 JFR.stop name = 1
1836:
Command executed successfully

结果是 二进制文件 完全看不懂 

Java11新特性

 java8 中的 jfr 可以处理,这个文件,或者更高版本的比如 jdk 12

jfr summary test.jfr //概况

jfr print --events CPULoad test.jfr  //查看内存使用率

jfr help print   //帮助

jfr print --events GarbageCollection test.jfr //查看 GC 记录

 看不懂?

掌握了  java 虚拟机内存管理 ,垃圾回收器是如何工作的,类加载器是如何工作的 就行了