这里写目录标题
一、JDK9
1、模块化系统(最主要的特性)
1、为什么引进模块化?
运行一个简单的hello world程序,在JDK8中你需要几百兆的JRE环境,有些jar可能并不是你运行程序所必须的,浪费了很多空间。但是在 JDK 9中,JDK被分成了大约94个modules,在用的时候引入你需要的module就行了。
2、什么是模块化
本质上讲,模块的概念就是package外再裹一层。
也就是说,用模块来管理各个package,通过声明某个package暴露,不声明默认就是隐藏。
3、实现的目标
- 减少内存的开销
- 只需必要模块,而非全部jdk模块,可简化各种类库和大型应用的开发和维护
- 改进javase平台,使其可以适应不同大小的计算设备
- 改进其安全性,可维护性,提高性能
4、使用
新建两个子模块,分别新建module-info
文件
工程1的module-info:
注意
1、项目的pom文件中导入了依赖,但是如果使用了模块化机制,如果想用依赖中的类,就先要导入依赖对应的模块,如果不使用模块化机制,依赖中的类是可以直接使用的。
2、只有用了module-info,才会以模块化的形式编译程序,这时,一个第三方jar才可以认为是一个Java module,目前Java生态中大多数的库还没有考虑加上module-info模块化,所以一个jar导出的是jar里的所有包
module demo {
//引用第三方模块,由于这些模块还没有考虑到加上module-info模块化,所以一个jar导出的是jar里的所有包
requires lombok;
requires spring.boot.autoconfigure;
requires spring.boot;
//导出模块
exports com.example.demo.bean;
}
工程2的module-info:
module javatest {
requires demo;
requires spring.boot.autoconfigure;
requires spring.boot;
requires aoptest;
}
通过这种机制,工程2中就能只用工程1中com.example.demo.bean下的类,而以前,可能是直接导入工程1的依赖,把工程1的所有类到都引入到工程2中来。
2、Jshell(java9开始)
下面JDK11有。
3、多版本兼容jar包
多版本兼容 JAR 功能能让你创建仅在特定版本的 Java 环境中运行库程序时选择使用的 class 版本。
https://www.jianshu.com/p/972c7d531094
4、接口中定义私有方法
面试题:抽象类和接口异同?
①声明方式不同:接口使用interface,抽象类使用class
②内部的结构不同
- jdk8之前:接口只能声明全局变量和抽象方法,但是抽象类就比较自由了,和普通的类差不多
- jdk8:接口可以声明静态方法和默认方法,并且是可以实现的
- jdk9:接口可以声明私有方法
③接口和抽象类都不能实例化,以多态的方式使用
④接口是多实现的,抽象类是单继承的
5、语法改进:砖石操作符使用升级
能够与匿名实现类共同使用砖石操作符。
例如:
public static void main(String[] args) {
//创建一个继承于hashset的匿名子类的对象
Set<String> set = new HashSet<>(){};
set.add("AA");
set.add("BB");
set.add("CC");
for (String s : set) {
System.out.println(s);
}
}
6、语法改进:try语句的升级
public static void main(String[] args) {
InputStreamReader reader=new InputStreamReader(System.in);
//jdk8是不允许这样做的
try(reader){
//读取数据的过程,略
reader.read();
}catch (IOException e){
}
}
7、String存储结构变更
以前底层是使用char[]
数组,jdk9改为字节数组byte[]
。(一个字符等于两字节等于16bit)
原因:大多数字符串对象包含的仅仅是拉丁字符(abcd/拼音文字等都属于拉丁字符),而事实上这些拉丁字符仅仅需要一个字节就能存储了,因此,如果使用char
数组存储拉丁字符,会浪费一半的空间。
问题:中文是两个字节,一个byte
是存不下的,怎么解决?
coder
变量,coder
代表编码的格式,目前String
支持两种编码格式LATIN1
和UTF16
。
LATIN1
需要用一个字节来存储。而UTF16
需要使用2个字节或者4个字节来存储。
注意:StringBuilder与StringBuffer也是更着改变了,底层也是用的byte[]
8、统一的jvm日志系统
二、JDK11
安装jdk11
IDEA切换到JDK11
1、Jshell(java9开始)
启动jshell就相当于进入 main方法,直接敲代码执行就可以了。
简而言之,使用 JShell,你可以输入代码片段并马上看到运行结果,然后就可以根据需要作出调整
C:\Users\Administrator>jshell
| Welcome to JShell -- Version 11.0.1
| For an introduction type: /help intro
jshell> System.out.print("asd");
asd
jshell> int n=200;
n ==> 200
jshell> System.out.print(n);
200
好处是可以使我们快速的调试测试代码,而不用去新建一个class文件,在new一个类,在写一个main方法,编译再运行。
/help
可以查看一些命令,包括如何退出jshell
等。/exit
/import
可以查看默认导入的包,也可以直接导import 包;
/
可以查看常用的指令
从外部加载源代码:
/open E:\HelloWorld.java
2、Dynamic Class-File Constants类文件新添的一种结构
Java
的类型文件格式将被拓展,支持一种新的常量池格式:CONSTANT_Dynamic,加载CONSTANT_Dynamic会将创建委托给bootstrap方法。- 目标:是降低开发新形式的可实现类文件约束带来的成本和干扰。
3、局部变量类型推断(var “关键字”)
- var是语法的一个改进,但它不是关键字。
- 局部变量类型推断就是左边的类型直接使用 var 定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型。
jshell> var b=new StringBuilder("123");
b ==> 123
jshell> System.out.print(b.getClass());
class java.lang.StringBuilder
注意点:
1、var a;
这样不可以,因为无法推断
2、类的属性的数据类型不可以使用var
主要用途,用在lambda
表达式中,在声明隐式类型的lambda
表达式的形参时允许使用var
,使用var
的好处是在使用lambda
表达式时给参数加上注解
(@Deprecated var x, @Nullable var y) -> x.process(y);
4、新加一些实用的API
4.1、创建只读集合of【jdk9就有】
自 Java 9 开始,Jdk 里面为集合(List/ Set/ Map)都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。
//快速创建不可改变集合
List<String> list1 = List.of("1", "2", "3");
System.out.println(list1);
//不能添加元素 Exception in thread "main" java.lang.UnsupportedOperationException
// list1.add("4");
List<String> list2 = Arrays.asList("1", "10");
System.out.println(list2);
//不能添加元素 Exception in thread "main" java.lang.UnsupportedOperationException
list2.add("100");
System.out.println(list2);
LIST.OF和ARRAYS.ASLIST区别:
- Arrays.asList返回可变的list,而List.of返回的是不可变的list,前者可以通过set方法修改,后者不行
- Arrays.asList支持null,而List.of不行
- Arrays.asList:数组的修改会影响原数组。
4.2、集合copyOf
示例1:
//使用of创建的集合是不可变类型的集合
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?
copyOf
方法会先判断来源集合是不是 AbstractImmutableList
类型的,如果是,就直接返回,如果不是,则调用of
创建一个新的集合。
使用of
和copyOf
创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException
异常。
上面演示了 List
的 of
和 copyOf
方法,Set
和 Map
接口都有。
4.3、增强Stream API【jdk9就有了】
1、ofNullable
允许传入null创建一个stream对象。
Stream.of()是不允许传入null来创建stream对象的。
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("a", "b", "c");
stream1.forEach(System.out::println);
System.out.println("------------------------------");
Stream<String> stream2 = Stream.of();//流中没有数据
stream2.forEach(System.out::println);
System.out.println("------------------------------");
//java.lang.NullPointerException 因为传入的null会被解析成一个数组对象,这个对象会进一步访问它的长度
// Stream<String> stream3 = Stream.of(null);
// stream3.forEach(System.out::println);
//可以传入一个null来创建流对象
Stream<Object> objectStream = Stream.ofNullable(null);
objectStream.forEach(System.out::println);
}
2、takeWhile
从流中获取判定器为真的元素,一旦遇到元素为假,就终止处理
public static void main(String[] args) {
Stream<Integer> stream1 = Stream.of(1,3,4,5,6);
//从流中获取判定器为真的元素,一旦遇到元素为假,就终止处理
Stream<Integer> integerStream = stream1.takeWhile(t -> t % 2 != 0);
integerStream.forEach(System.out::println);
}
1
3
3、dropWhile
从Stream中依次删除满足条件的元素,直到不满足条件为止结束删除
stream1 = Stream.of(1,3,4,5,6);
Stream<Integer> integerStream1 = stream1.dropWhile(t -> t % 2 != 0);
integerStream1.forEach(System.out::println);
4
5
6
4、流的迭代
//流的迭代,创建流
Stream<Integer> iterate = Stream.iterate(1, t -> (2 * t) + 1);
iterate.limit(10).forEach(System.out::println);
System.out.println("==============================================");
//有限的迭代,类似for循环
Stream<Integer> iterate1 = Stream.iterate(1, t -> t < 1000, t -> (2 * t) + 1);
iterate1.forEach(System.out::println);
4.4、新增的字符串API
判断字符串是否为空白:isBlank
String s=" ";
System.out.println(s.isBlank());
String s1=" \n \r \t ";
System.out.println(s1.isBlank());
true
true
去除首尾空白:
strip
:去除字符串首尾的空白,包括英文和其他所有语言中的空白字符
stripLeading
:去除字符串首部的空白
stripTrailing
:去除字符串尾部的空白
trim
:去除字符串首尾的空白字符,只能去除码值小于等于32
的空白字符
String s2=" asd ";
System.out.println(s2.strip());
String s3="\r \t asd \n";
System.out.println(s3.strip());
复制字符串:
String str="java";
String repeat = str.repeat(5);
System.out.println(repeat);
javajavajavajavajava
行数统计:
String line="A\nB\n";
long count = line.lines().count();
System.out.println(count);
4.5、String行数统计的应用
以前读取一个文件的内容,需要使用转换流和缓冲流
StringBuffer buffer = new StringBuffer();
String line; // 用来保存每行读取的内容
BufferedReader bufferreader = new BufferedReader(new InputStreamReader(new FileInputStream("src\\main\\java\\com\\example\\springboot\\aoptest\\jdk11\\StringTest.java")));
line = bufferreader.readLine(); // 读取第一行
while (line != null) { // 如果 line 为空说明读完了
buffer.append(line); // 将读到的内容添加到 buffer 中
buffer.append("\n"); // 添加换行符
line = bufferreader.readLine(); // 读取下一行
}
//buffer中的内容写出来
bufferreader.close();
System.out.println(buffer) ;
现在使用String新API:
将文件中的内容直接全部放到一个String中,然后使用流,对字符串进行行的提取,就可以快速的读取文件中的内容了。
可以省略转换流和缓存流,完全可以替代BufferReader,更加的方便和好用
FileInputStream fis = new FileInputStream("src\\main\\java\\com\\example\\springboot\\aoptest\\jdk11\\StringTest.java");
//available方法可以在读写操作前先得知数据流里有多少个字节可以读取
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();//关闭流
//把内容一次性读到String中
String line=new String(buffer);
//一共有几行,lines返回从此字符串中提取的行的流,由行终止符分隔。
long count = line.lines().count();
System.out.println(count);
//遍历每一行
line.lines().forEach(System.out::println);
4.6、optional增强
//of:方法中如果传入null,会抛出空指针异常
//ofNullable:可以兼容空指针
//orElse:如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)
Object abc = Optional.ofNullable("123").orElse("abc");
System.out.println(abc);
//orElseThrow
Object object1 = Optional.ofNullable(null).orElseThrow(()->new RuntimeException("xxx不能为空"));
System.out.println(object1);
4.7、InputStream 加强
transferTo,可以用来将数据直接传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法。
例如:
文件复制
public static void main(String[] args) throws IOException {
//流会自动关闭
try(var is = new FileInputStream("src\\main\\java\\com\\example\\springboot\\aoptest\\jdk11\\file");
var os=new FileOutputStream("file2");
){
//把输入流中的所有数据直接自动地复制到输出流中
is.transferTo(os);
}
}
非常实用的方法,不需要在写一个循环,也不需要写一个缓冲区。
5、标准java异步HTTP客户端【jdk9开始引入】
以前使用apache
的httpclient
:
public static void main(String[] args) {
//apache的httpclient
String url="http://localhost:8080/aopController?id=1&name=lzj";
String result = httpGet(url);
System.out.println(result);
}
//httpGet
private static String httpGet(String url) {
// get请求返回结果
String parse = null;
// 创建httpclient对象
CloseableHttpClient client = HttpClients.createDefault();
// 创建get方式请求对象
HttpGet request = new HttpGet(url);
try {
// 设置通用的请求属性,设置header信息
request.addHeader("content-type", "application/json");
// 获取相应数据,这里可以获取相应的数据
HttpResponse response = client.execute(request);
// 判断网络请求是否成功,成功的状态码200
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// 读取服务器返回过来的json字符串数据
HttpEntity entity = response.getEntity();
parse = EntityUtils.toString(entity, "utf-8");
} else {
throw new RuntimeException("xxx接口失败!!");
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//释放连接
request.releaseConnection();
}
return parse;
}
JDK
的HttpClient(jdk11)
:
java.net.http.HttpClient
是 jdk11 中正式启用的一个 http 工具类(其实早在 jdk9 的时候就已经存在了,只是处于孵化期),官方想要取代 HttpURLConnection
和 Apache HttpClient
等比较古老的开发工具。
public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
//newHttpClient:返回一个默认设置的HttpClient
HttpClient httpClient = HttpClient.newHttpClient();
//如果想要自定义设置使用newBuilder
// HttpClient httpClient1 = HttpClient.newBuilder()
// .version(HttpClient.Version.HTTP_2)//http 协议版本 1.1 或者 2
// .connectTimeout(Duration.ofMillis(5000))//连接超时时间,单位为毫秒
// .followRedirects(HttpClient.Redirect.NEVER)//连接完成之后的转发策略
// .executor(Executors.newSingleThreadExecutor())//指定线程池
// //认证,默认情况下 Authenticator.getDefault() 是 null 值,会报错
// //.authenticator(Authenticator.getDefault())
// //代理地址
// //.proxy(ProxySelector.of(new InetSocketAddress("http://www.baidu.com", 8080)))
// //缓存,默认情况下 CookieHandler.getDefault() 是 null 值,会报错
// //.cookieHandler(CookieHandler.getDefault())
// .build();//创建完成
//HttpRequest 是发起请求的主体配置
HttpRequest request = HttpRequest.newBuilder()
//存入消息头
//消息头是保存在一个TreeMap里的
.header("Content-Type", "application/json")
.version(HttpClient.Version.HTTP_2)//http 协议版本
//url地址,也可以直接在newBuilder()中设置
.uri(URI.create("http://localhost:8080/aopController?id=1&name=lzj"))
//超时时间
.timeout(Duration.ofMillis(5000))
//发起一个 post 消息,需要存入一个消息体
//.POST(HttpRequest.BodyPublishers.ofString("hello"))
//发起一个 get 消息,get 不需要消息体
.GET()
.build();
//同步方式发起请求
// HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
//异步方式发起请求
CompletableFuture<HttpResponse<String>> httpResponseCompletableFuture = httpClient.sendAsync(request, BodyHandlers.ofString());
HttpResponse<String> response = httpResponseCompletableFuture.get();
System.out.println(response.body());
}
6、移除的一些其他内容
移除项:
移除了com.sun.awt.AWTUtilities
移除了sun.misc.Unsafe.defineClass,
使用java.lang.invoke.MethodHandles.Lookup.defineClass来替代
移除了Thread.destroy()以及 Thread.stop(Throwable)方法
移除了sun.nio.ch.disableSystemWideOverlappingFileLockCheck、sun.locale.formatasdefault属性
移除了jdk.snmp模块
移除了javafx,openjdk估计是从java10版本就移除了,oracle jdk10还尚未移除javafx,而java11版本则oracle的jdk版本也移除了javafx
移除了Java Mission Control,从JDK中移除之后,需要自己单独下载
移除了这些Root Certificates :Baltimore Cybertrust Code Signing CA,SECOM ,AOL and Swisscom
废弃项:
-XX+AggressiveOpts选项
-XX:+UnlockCommercialFeatures
-XX:+LogCommercialFeatures选项也不再需要
7、更简化的编译运行程序【jdk9引入】
以前编译运行程序,需要先javac编译,然后再用命令java才能运行程序。
现在直接使用java就行
public class HelloJava{
public static void main(String[] args){
System.out.println("hello");
}
}
G:\Desktop\新建文本文档>java HelloJava.java
hello
注意点 :
1)执行源文件中的第一个类, 第一个类必须包含主方法
2)并且不可以使用别源文件中的自定义类, 本文件中的自定义类是可以使用的.
8、Epsilon垃圾收集器【实验性质的】
A NoOp Garbage Collector
:一个没操作的垃圾收集器。
JDK
上对这个特性的描述是: 开发一个处理内存分配(可以创建对象),但不实现任何实际内存回收机制的GC
, 一旦可用堆内存用完, JVM
就会退出。
如果有System.gc()
调用, 实际上什么也不会发生(这种场景下和-XX:+DisableExplicitGC
效果一样), 因为没有内存回收, 这个实现可能会警告用户尝试强制GC
是徒劳.
用法 : -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
jvm参数:-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
class Garbage {
int n = (int)(Math.random() * 100);
//这个方法是GC在第一次清除当前对象时,会调用一次,只调用一次
@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("程序结束");
}
}
结果:不会进行垃圾回收,内存用完,直接使程序停止
Terminating due to java.lang.OutOfMemoryError: Java heap space
使用这个选项的原因 :
提供完全被动的GC实现, 具有有限的分配限制和尽可能低的延迟开销,但代价是内存占用和内存吞吐量.
主要用途如下 :
- 性能测试(它可以帮助过滤掉GC引起的性能假象)
- 内存压力测试(例如,知道测试用例 应该分配不超过1GB的内存, 我们可以使用-Xmx1g –XX:+UseEpsilonGC,如果程序有问题, 则程序会崩溃,比如:内存泄漏)
- 非常短的JOB任务(对象这种任务, 接受GC清理堆那都是浪费空间)
- VM接口测试
- Last-drop 延迟&吞吐改进
9、ZGC,重要【实验性质的,不建议用到生产环境】
ZGC
:一个短延迟的垃圾回收器。jdk11
的windows
版不支持ZGC
,linux
下才能使用
GC
暂停时间不会超过10ms
- 既能处理几百兆的小堆, 也能处理几个T的大堆(
OMG
) - 和
G1
相比, 应用吞吐能力不会下降超过15%
- 为未来的
GC
功能和利用colord
指针以及Load barriers
优化奠定基础 - 初始只支持
64
位系统
ZGC
的设计目标是:支持TB
级内存容量,暂停时间低(<10ms
),对整个程序吞吐量的影响小于15%
。 将来还可以扩展实现机制,以支持不少令人兴奋的功能,例如多层堆(即热对象置于DRAM
和冷对象置于NVMe
闪存),或压缩堆。
ZGC
是一个并发, 基于region
, 压缩型的垃圾收集器, 只有root
扫描阶段会STW
, 因此,GC
停顿时间不会随着堆的增长和存活对象的增长而变长.
ZGC与G1的停顿时间对比:
ZGC : avg 1.091ms max:1.681
G1 : avg 156.806 max:543.846
用法 : -XX:+UnlockExperimentalVMOptions –XX:+UseZGC
9.1、ZGC的工作原理
为了实现其目标,ZGC
给Hotspot Garbage Collectors
增加了两种新技术:着色指针和读屏障。
着色指针
32位的jdk用不到TB级内存容量,只能用到最多4G。
64位系统是使用8字节,来作为内存编址,因此,数据量是很大的,可支持的内存也会变的非常的大,远远大于TB级,保存引用的数据量由4个字节扩展到8字节,多出来的数据,就会给我们无限的可能,前四个字节4G,稍微往后,高位用上一点点,那么内存范围就会变化很大
着色指针是一种将信息存储在指针(或使用Java术语引用)中的技术。
因为在64
位平台上(ZGC
仅支持64
位平台),指针可以处理更多的内存,因此可以使用一些位来存储状态。
https://blog.csdn.net/hejuecan5759/article/details/106390994/
https://www.cnblogs.com/fiftyonesteps/p/12568737.html
10、完全支持Linux容器(包括Docker)
许多运行在Java虚拟机中的应用程序(包括Apache Spark和Kafka等数据服务以及传统的企业应用程序)都可以在Docker容器中运行。但是在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行JVM程序在设置内存大小和CPU使用率后,会导致应用程序的性能下降。这是因为Java应用程序没有意识到它正在容器中运行。随着Java 10的发布,这个问题总算得以解决,JVM现在可以识别由容器控制组(cgroups)设置的约束。
如何适应docker,这些都是自动完成的。
11、支持G1上的并行完全垃圾收集
对于 G1,相比于 JDK 8,升级到 JDK 11 即可免费享受到:并行的Full GC,快速的CardTable 扫描,自适应的堆占用比例调整(IHOP),在并发标记阶段的类型卸载等等。这些都是针对G1 的不断增强,其中串行Full GC 等甚至是曾经被广泛诟病的短板,你会发现GC配置和调优在 JDK11 中越来越方便。
12、Java Flight Recorder(JFR)
Flight Recorder
源自飞机的黑盒子,会把jvm
运行的细节全部记录下来,最终记录到一个文件中,通过分析文件,能够知道jvm
内部是怎么运作的,比如:GC怎么工作的,类什么时候加载,对象什么时候创建等等。
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
JFR 是一套集成进入 JDK、JVM 内部的事件机制框架,通过良好架构和设计的框架,硬件层面的极致优化,生产环境的广泛验证,它可以做到极致的可靠和低开销。在 SPECjbb2015 等基准测试中,JFR 的性能开销最大不超过 1%,所以,工程师可以基本没有心理负担地在大规模分布式的生产系统使用,这意味着,我们既可以随时主动开启 JFR 进行特定诊断,也可以让系统长期运行 JFR,用以在复杂环境中进行“After-the-fact”分析。还需要苦恼重现随机问题吗?JFR 让问题简化了很多。
使用:
//找出java进程
G:\Desktop\资料\学习资料\jdk11新特性\笔记源码\code>jps -l
8592
10740 org.jetbrains.idea.maven.server.RemoteMavenServer
11044 org.jetbrains.jps.cmdline.Launcher
9236 jdk.jcmd/sun.tools.jps.Jps
9528 JFRTest
//打开jfr
G:\Desktop\资料\学习资料\jdk11新特性\笔记源码\code>jcmd 9528 JFR.start
9528:
Started recording 1. No limit specified, using maxsize=250MB as default.
Use jcmd 9528 JFR.dump name=1 filename=FILEPATH to copy recording data to file.
//把数据导出来
G:\Desktop\资料\学习资料\jdk11新特性\笔记源码\code>jcmd 9528 JFR.dump filename=test.jfr name=1
9528:
Dumped recording "1", 300.0 kB written to:
G:\Desktop\资料\学习资料\jdk11新特性\笔记源码\code\test.jfr
//停止jfr
G:\Desktop\资料\学习资料\jdk11新特性\笔记源码\code>jcmd 9528 JFR.stop name=1
9528:
Stopped recording "1".
查看导出来的jfr
文件:jdk11
没有提供jfr
命令查看,jdk12
以上才提供。