JDK14特性

JDK14

  • 1 概述
  • 2 语法层面的变化
    • 1_instanceof的模式匹配(预览)
    • 2_switch表达式(标准)
    • 3_文本块改进(第二次预览)
    • 4_Records 记录类型(预览 JEP359)
  • 3 API层面的变化
  • 4 关于GC
    • 1_G1的NUMA内存分配优化
    • 2_弃用Serial+CMS,ParNew+Serial Old
    • 3_删除CMS
    • 4_ZGC on macOS and Windows
  • 4 其他变化
    • 1_友好的空指针异常提示
    • 2_JAVA打包工具 JEP343(孵化阶段)
    • 3_JFR事件流
    • 4_外部存储器API (孵化阶段)
    • 5_非易失性映射字节缓冲区

1 概述

Oracle在2020年3月17日宣布JAVA14 全面上市,JAVA14通过每六个个月发布一次新功能,为企业和开发人员社区提供增强功能,继续了Oracle加快创新的承诺.

最新的JAVA开发工具包提供了新功能,其中包括两项备受期待的新预览功能,实例匹配的匹配模式(JEP 305) 和记录(JEP 359),以及文本块的第二个预览(JEP 368),此外,最新的JAVA版本增加了对switch表达式的语言支持,公开了,用于持续监控JDK Flight Recorder数据的新API。

将低延迟的Z垃圾收集器的可用性扩招到了macOS和Windows,并在孵化器模块中添加了包装完备的java应用程序和新的外部内存访问API,以安全高效的访问JAVA对外部的内存。

我们可以在openjdk灌完中观察到JDK14发布的详细官方计划和具体新特性详情,地址如下

https://openjdk.java.net/projects/jdk/14/

JAVA14 一共发行了16个JEP(JDK Enhancement Proposals,JDK 增强提案)

在这里插入图片描述

语言特性7项目

  • switch表达式(标准)
  • 友好的空指针异常
  • 非易失性字节缓冲区
  • record
  • instanceof模式匹配
  • 文本块改进 二次预览
  • 外部存储API

垃圾回收修改

  • G1的NUMA内存分配优化

新增工具

  • JAVA打包工具 孵化
  • JFR事件流

增加废弃和移除

  • MacOS系统上的ZGC试验
  • windows系统上的ZGC实验
  • 弃用Parallel Scavenge 和Serial Old垃圾收集算法
  • 弃用Solaris和SPCRC端口
  • 移除CMS垃圾收集器
  • 删除Pack200工具和API

2 语法层面的变化

1_instanceof的模式匹配(预览)

Pattern Matching for instanceof (Preview)

以往我们使用instanceof运算符都是先判断,然后在进行强转,例如我们查看String的equals方法源码

 public boolean equals(Object anObject) {if (this == anObject) {return true;}// 先进行类型的判断 if (anObject instanceof String) {// 然后进行强转String aString = (String)anObject;if (!COMPACT_STRINGS || this.coder == aString.coder) {return StringLatin1.equals(value, aString.value);}}return false;}

需要先判断类型,然后强转,还要声明一个本地变量,语法比较麻烦.比较理想的状态是,在执行类型检测的时候同时执行类型转换

JEP305 新增了使 instanceof 运算符具有匹配的能力. 模式匹配能够是程序的通用逻辑更加简洁,代码更加简单,同时在做类型判断和类型转换的时候也更加安全.详情如下

JAVA14 提供了新的解决方案: 新的instanceof模式匹配,新的模式匹配语法是: 在instanceof的类型之后添加了变量. 如果对obj的类型检查通过,obj会被转换成后面的变量表示的数据类型. 数据类型的声明仅仅书写一次即可

		Object obj ="hello java";if(obj instanceof String str){System.out.println(str);}else{System.out.println("not a String");}

上述语法的判断逻辑时,如果obj是String类型,则会转换为后面的str,如果不是,则执行else,注意,此时的str仅仅是if语句块里的局部变量,在else语句块中不可用

        Object obj ="hello java";// 这里做的是取反运算if(!(obj instanceof String str) ){System.out.println("not a String");//System.out.println(str);// 这里不能使用str}else{System.out.println(str);// 这里可以使用str}

但是如果if语句中使用了! 这种取反运算,那么逻辑上就是相反的,这个时候else才是相当于成功转换了,所以在else中可以使用str,if中不可以使用str

		Object obj =new Date();// "hello java";if(obj instanceof String str && str.length()>2){System.out.println(str);}else{System.out.println("not a String or length <=2");}

上述语句块中,如果if中的判断逻辑比较复杂,是可以在后续的其他条件中使用str变量进行判断的,但是注意这里的运算符是短路与运算,就是要保证后面在使用str时,已经完成了转换,如果使用短路或运算,无法保证str是可以成功转换的,是不允许的,如下面的代码,就是错的

		Object obj =new Date();// "hello java";if(obj instanceof String str || str.length()>2){System.out.println(str);}else{System.out.println("not a String or length <=2");}

总之: if语句块中的小括号内,要保证成功的进行了转换才可以在if语句库中使用转换的对象,否则不可以

通过这个模式匹配,我们可以简化在类中重写的equals方法

import java.util.Objects;
class Person{private String pname;private Integer page;public Person(String pname,Integer page){this.pname =pname;this.page=page;}@Overridepublic boolean equals(Object obj){return obj instanceof Person p && Objects.equals(this.pname,p.pname)&& Objects.equels(this.page,p.page);}
}

2_switch表达式(标准)

java的Switch语句是一个一直在变化的语法,可能是因为之前的不够强大,在JAVA14中,我们依然可以看到对于switch的语法优化.

我们简单整理一下switch语句在各个版本中的特点:

  • JAVA5 switch变量类型可以使用枚举了

  • JAVA7 switch变量类型中可以使用String

  • JAVA11 switch语句可以自动省略break导致的贯穿提示警告 case L ->

  • JAVA12 switch语句可以作为表达式,用变量接收结果,可以省略break

  • JAVA13 switch中可以使用yield关键字停止switch语句块

JAVA14 JEP361switch表达式(标准)是独立的,不依赖于JEP 325 和 JEP 354,也就是说这里开始,之前学习的switch语句的语法成为一个正式的标准.未来是否有更多的改进,我们可以拭目以待。

JDK12对缺省break的贯穿弱点进行了改进,case: 改成 case L -> ,这样即使不写也不会贯穿了,而且可以作为表达式返回结果。

var grade ="a";
var res =switch(grade){case "a" -> "优秀";case "b" -> "良好";case "c" -> "一般";case "d" -> "及格";default -> "no such grade";
}

JAVA12 开始也可以进行多值匹配的支持。

var grade ="a";
var res =switch(grade){case "a","b" -> "优秀";case "c" -> "一般";case "d" -> "及格";default -> "no such grade";
}

JAVA13开始可以使用 yield返回结果,这里的case后面仍然是:

String x = "3";
int i = switch (x) {case "1":yield 1;case "2":{System.out.println("");yield 2;}default:yield 3;
};
System.out.println(i);

3_文本块改进(第二次预览)

Text Blocks(Second Preview)

问题

文本块是在JAVA13中开始了第一次的预览,目标是在字符串中可以更好的表达 HTML XML SQL或者JSON格式的字符串,减少各种的不相关一些空格换行符号,字符串转义和字符串加号的拼接,在JAVA14中,增加了两个escape sequence ,分别是 \ (取消换行操作) 与\s escape sequence(增加空格),文本块进行了第二次预览,进一步调了JAVA程序书写大段字符串文本的可读性和方便性

目标

  • 简化跨越多行的字符串,避免对换行等特殊字符进行转义,简化java程序
  • 增强java程序中用字符串表示其他语言代码的可读性
  • 解析新的转义序列
String textBlock= """<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head>\s\<body></body>\s</html>""";System.out.println(textBlock);

4_Records 记录类型(预览 JEP359)

通过Record增强java编程语言,Record提供了一种紧凑的语法来声明类,这些类是浅层不可变数据的透明持有者.

问题分析

我们经常听到这样的抱怨:“JAVA太冗长”,“JAVA规矩多”. 最明显的就是最为简单数据载体的类,为了写一个数据类,开发人员必须编写许多低价值,重复,且容易出错的代码,构造函数,getter setter访问器,equals,hashcode,toString这些东西,尽管IDE可以提供一些插件和手段优化,但是仍然没有改变这些代码依然存在,需要操作的事实

传统的类如下

class Person{private Integer pid;private String pname;private Integer page;@Overridepublic String toString() {return "Person{" +"pid=" + pid +", pname='" + pname + '\'' +", page=" + page +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(pid, person.pid) && Objects.equals(pname, person.pname) && Objects.equals(page, person.page);}@Overridepublic int hashCode() {return Objects.hash(pid, pname, page);}public Integer getPid() {return pid;}public void setPid(Integer pid) {this.pid = pid;}public String getPname() {return pname;}public void setPname(String pname) {this.pname = pname;}public Integer getPage() {return page;}public void setPage(Integer page) {this.page = page;}public Person() {}public Person(Integer pid, String pname, Integer page) {this.pid = pid;this.pname = pname;this.page = page;}
}

就算是使用IDE的快捷键,这些代码也是臃肿的

Records记录类型语法

Record是java的一种新的类型,同枚举一样,Record也是对类的一种限制,Record放弃了类通常享有的特性:将API和表示解耦。

但是作为回报,record使数据类型变得非常简洁,一般可以帮助我们定义一些简单的用于传递数据的实体类

一个record具有名称和状态描述,状态描述声明了record的组成部分

record Person(String name ,int age){}

因为record在与以上是数据的简单透明持有者,所以record会自动获取很多的标准成员

  • 状态声明中的每个成员,都是一个private final的字段,属性设置值则不可修改
  • 状态声明中的每个组件的公共读取访问方法,该方法和组件具有相同的名字,get方法和属性名一致
  • 一个公共的构造函数,其签名与状态声明相同,构造方法和签名合二为一
  • equals和hashcode的实现
  • toString的实现
  • record提供的默认是一个全参的构造器

测试代码如下

package com.shenyang.test;import java.util.Objects;/*** @Author: shenyang* @Description: JDK14K*/
public class Test2 {public static void main(String[] args) {Person p =new Person(1,"张三",10);Person p2 =new Person(1,"张三",10);System.out.println(p.pname());System.out.println(p);System.out.println(p.hashCode());System.out.println(p2.hashCode());System.out.println(p.equals(p2));}
}
record Person(Integer pid,String pname ,Integer page){};

测试结果如下

张三
Person[pid=1, pname=张三, page=10]
24022530
24022530
true

Records的一些限制

records类是隐含的final类,并且不是抽象类,records不能拓展任何类,不能被继承,声明的任何其他字段都必须是静态的。

records的API仅仅能由其状态描述定义(通过属性定义)

在record中声明额外的变量类型

也可以显示声明从状态描述自动派生的任何成员,可以在没有正式参数列表的情况下声明构造函数,并且在正常的构造函数主体正常完成是调用隐式初始化,这样就可以在显示构造函数中仅执行其参数的验证逻辑,并且省略字段的初始化。

测试代码如下

package com.shenyang.test;import java.util.Objects;/*** @Author: shenyang* @Description: JDK14K*/
public class Test2 {public static void main(String[] args) {Person p =new Person(1,"张三",10);Person p2 =new Person(1,"张三",10);System.out.println(p.pname());System.out.println(p);System.out.println(p.hashCode());System.out.println(p2.hashCode());System.out.println(p.equals(p2));}
}
record Person(Integer pid,String pname ,Integer page){// 定义额外的变量必须是静态的,不能定义成员变量private static String name;public static void setName(String name){Person.name=name;}// 可以定义其他实例方法public void eat(){System.out.println("eat");}// 可以定义其他静态方法public static void methodA(){System.out.println("methoA");}// 这里是构造函数,默认就是全参的构造函数,和record声明的参数列表是一致的,// 这里可以使用全参构造函数中的所有参数// 在这里会默认执行参数给属性赋值操作,就是在这里默认会有this.pid=pid,this.pname=pname,this.page=pagepublic Person{System.out.println(pid);System.out.println(pname);System.out.println(page);}
};

3 API层面的变化

4 关于GC

1_G1的NUMA内存分配优化

NUMA-Aware Memory Allocation for G1

NUMA

NUMA就是非统一内存访问架构(英语:non-uniform memory access,简称NUMA),是一种为多处理器的电脑设计的内存架构,内存访问时间取决于内存相对于处理器的位置。

在NUMA下,处理器访问它自己的本地内存的速度比非本地内存(内存位于另一个处理器,或者是处理器之间共享的内存)快一些。

如下图所示,Node0中的CPU如果访问Node0中的内存,那就是访问本地内存,如果它访问了Node1中的内存,那就是远程访问,性能较差:

在这里插入图片描述

非统一内存访问架构的特点是:

  • 被共享的内存物理上是分布式的,所有这些内存的集合就是全局地址空间。

  • 所以处理器访问这些内存的时间是不一样的,显然访问本地内存的速度要比访问全局共享内存或远程访问外地内存要快些。

  • 另外,NUMA中内存可能是分层的:本地内存,群内共享内存,全局共享内存。

目标

JEP345希望通过实现NUMA-aware的内存分配,改进G1在大型机上的性能。

现代的multi-socket服务器越来越多都有NUMA,意思是,内存到每个socket的距离是不相等的,内存到不同的socket之间的访问是有性能差异的,这个距离越长,延迟就会越大,性能就会越差!(https://openjdk.java.net/jeps/345)

G1的堆组织为固定大小区域的集合。一个区域通常是一组物理页面,尽管使用大页面(通过 -XX:+UseLargePages)时,多个区域可能组成一个物理页面。

如果指定了+XX:+UseNUMA选项,则在初始化JVM时,区域将平均分布在可用NUMA节点的总数上。

在开始时固定每个区域的NUMA节点有些不灵活,但是可以通过以下增强来缓解。为了为mutator线程分配新的对象,G1可能需要分配一个新的区域。它将通过从NUMA节点中优先选择一个与当前线程绑定的空闲区域来执行此操作,以便将对象保留在新生代的同一NUMA节点上。如果在为变量分配区域的过程中,同一NUMA节点上没有空闲区域,则G1将触发垃圾回收。要评估的另一种想法是,从距离最近的NUMA节点开始,按距离顺序在其他NUMA节点中搜索自由区域。

该特性不会尝试将对象保留在老年代的同一NUMA节点上。

JEP 345专门用于实现G1垃圾收集器的NUMA支持,仅用于内存管理(内存分配),并且仅在Linux下。对于NUMA体系结构的这种支持是否也适用于其他垃圾回收器或其他部分(例如任务队列窃取),尚不清楚。

2_弃用Serial+CMS,ParNew+Serial Old

由于维护和兼容性测试的成本,在JDK8时将Serial+CMS,ParNew+Serial Old这两个组合声明为废弃(JEP173),并在JDK9中完全取消了这些组合的支持(JEP214)

在这里插入图片描述

ParallelScavenge+SerialOld GC 的GC组合要被标记为Deprecate了

官方给出的理由

这个GC组合需要大量的代码维护工作,并且,这个GC组合很少被使用.因为它的使用场景应该是一个很大的Young区和一个很小的Old区,这样的话,Old区用SerialOld GC去收集停顿时间才可以勉强被接受

废弃了Parallelyoung generationGC 与SerialOldGC组合 (-XX:+UseParallelGC -XX:-UseParallelOldGC 配合开启),现在使用-XX:+UseParallelGC -XX:-UseParallelOldGC或者使用 -XX:-UseParallelOldGC会出现如下警告

在这里插入图片描述

3_删除CMS

删除CMS垃圾回收器

自从G1出现后,CMS在JDK9中就被标记为Deprecate了

CMS弊端

  • 会产生内存碎片,导致并发清除后,用户线程可用空间不足(标记清除算法产生,需要整理算法解决)
  • 既然强调了并发(Concurrent) CMS收集器对于CPU资源非常敏感,导致吞吐量降低
  • CMS收集器无法处理浮动垃圾(用户线程和垃圾回收线程并发执行,回收时用户线程产生新的垃圾)

当CMS停止工作时,会把 Serial Old GC作为备选方案,而它是JVM中性能最差的垃圾收集方式,停顿几秒甚至十秒都有可能

移除了CMS垃圾收集器,如果继续在JDK14中使用-XX:+UseConcMarkSweepGC 不会报错,仅仅给出一个warning警告

warning: Ignoring option UseConcMarkSweepGC; support was removed in 14.0

其他垃圾收集器

G1回收器hotSpot已经默认使用有几年了,我们还看到两个新的GC JAVA11中的ZGC和openJDK12中的Shenandoah,后两者主要特点是:低停顿时间

Shenandoah非Oracle官方发布的,是OpenJDK于JAVA12发布的

收集器名称运行时间总停顿时间最大停顿时间平均停顿时间
Shenandoah387.602s320ms89.79ms53.01ms
G1312.052s11.7s1.24s450.12ms
CMS286.264s12.78s4.39s852.26ms
ParallelScavenge260.092s6.59s3.04s823.75ms

4_ZGC on macOS and Windows

JAVA14之前,ZGC仅仅支持Linux

基于一些开发部署和测试的需要,ZGC在JDK14中支持在macOS 和windows,因此许多桌面级应用可以从ZGC中受益,目前还是一个实验性版本,要想在macOS 和windows上使用

ZGC与Shenandoah目标非常相似,都是在尽量减少吞吐量的情况下,实现对任意堆大小(TB级)都可以把垃圾收集器停顿时间限制在10毫秒以内的低延迟时间

ZGC 收集器是一款基于Region内存布局的,暂时不设分代的,使用了读屏障,染色指针和内存多重映射等技术来实现并发的标记压缩算法,以低延迟为首要目标的一款垃圾收集器.现在想在macOS 和windows上使用ZGC, 方式如下

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

关于ZGC的一些测试数据

在这里插入图片描述

在这里插入图片描述

ZGC目前还处于一个实验状态,但是性能非常亮眼,未来将在服务端,大内存,低延迟应用上作为首选的垃圾收集器

4 其他变化

1_友好的空指针异常提示

NullpointerException是java开发中经常遇见的问题,在JDK14之前的版本中,空指针异常的提示信息就是简答的null,并不会告诉我们更加有用的信息,知识根据异常产生的日志来进行查找和处理,对于很长的引用来说,很难定位到具体是哪个对象为null.

演示适用情况

public class Test2 {public static void main(String[] args) {Person p =new Person();p.cat.eat();}
}class Person{public Cat cat;
}
class Cat {public void eat(){}
}

上面的代码在调用eat方法时就会出现空指针异常

在这里插入图片描述

这种提示其实并不是很详细,我们可以在运行代码的时候,加上一段配置,用以展示比较友好的控制成提示信息

-XX:+ShowCodeDetailsInExceptionMessages

输出的信息如下所示

在这里插入图片描述

其他适用情况

但是对于更复杂的代码,不适用调用器就无法确定是哪个变量为空

a.b.c.d=100;

仅仅使用文件名和行数,并不能精确的提示到底是哪个变量为null

访问多维数组也会发生类似的情况

a[j][j][k]=99;

这里如果发生npe文件名和行号也是无法精确指出到底是哪一层的数组出现了空指针

再例如

a.i=b.j;

这里出现了npe 如果仅凭文件名和行号,无法确定到底是a的问题还是b的问题

NPE也可能在方法调用中传递

x().y().i=99;

这里如果出现了NPE,那么仅凭行号和文件名也是无法确定到底是x方法还是y方法的问题

接下来简单演示一个代码

/*** @Author: shenyang* @Description: JDK14K*/
public class Test2 {public static void main(String[] args) {A a =new A();B b=null;a.i=b.j;}
}class A{int i;
}
class B{int j;
}

如果没有友好提示

在这里插入图片描述

如果有友好提示

在这里插入图片描述

这时就可以发现,是j的问题,那么其实就是b对象的问题

2_JAVA打包工具 JEP343(孵化阶段)

该特征旨在创建一个用于打包独立java应用程序的工具.JAVA应用的打包和分发一直都是个老大的难题. 用户希望JAVA引用的安装和运行方式和其他应用有相似的体验. 比如,在windows上只需要双击文件就可以运行. JAVA平台本身没有提供实用的工具解决这个问题. 通常都依赖第三方的工具完成,这个JEP的目标就是创建一个简单的JAVa打包工具jpackage. 相对于第三方工具,jpackage只适用于比较简单的场景,不过对很多应用来说已经足够好了.

该jpackage工具将java的应用程序打包到特定的平台的程序包中,该程序包包含所必须的依赖. 该应用程序可以作为普通的jar文件或者模块的集合提供,受支持的特定平台的软件包格式为:

1 Linux deb或者 rpm

2 maxOS: pkg和dmg

3 windows L msi和exe

默认情况下,jpackage以最适合其运行系统的格式生成软件包

项目打包-非模块化项目

如果有一个包含jar文件的应用程序,所有的应用程序都位于一个名为lib 的目录总,并且lib/main.jar包含主类,可以通过如下命令打包

$ jpackage --name myapp -- input lib --main-jar main.jar

将以本地系统的默认格式打包应用程序,将生成的打包文件保留到当前目录中. 如果MANIFEST.MF文件中没有main.jar.没有Main-Class属性,则必须显式指定主类

$ jpackage --name myapp --input lib --main-jar main.jar \ --main-class myapp.Main

软件包的名称将为没有app ,尽管软件包文件本身的名称将更长,并以软件包类型皆为,该软件包将包括该应用程序的启动器,也称为myapp .要启动应用程序,启动程序将会从输入目录复制的每个jar文件放在jvm的类路径上

如果您希望默认格式以外的其他格式制作软件包,请使用 --type选项. 例如,要在macOS 上生成pkg文件而不是dmg文件

$ jpackage --name myapp --input lib --main-jar main.jar --type pkg

项目打包-模块化项目

如果您有一个模块化应用程序,该程序有目录中的模块化jar文件或JMOD文件组成,并且模块中lib包含主类myAPP,则命令为

$ jpackage -name myapp --moudule-path lib -m myapp

如果myAPP模块未标识主类,则必须再次明确

$ jpackage -name myapp --moudule-path lib -m myapp/myapp.Main

3_JFR事件流

简介

Java Flight Recorder(JFR)是JVM的诊断和性能分析工具。

JAVA14之前只能做离线的分析,现在可以做实时的持续监视

它可以收集有关JVM以及在其上运行的Java应用程序的数据。JFR是集成到JVM中的,所以JFR对JVM的性能影响非常小,我们可以放心的使用它。

一般来说,在使用默认配置的时候,性能影响要小于1%。

JFR的历史很久远了。早在Oracle2008年收购BEA的时候就有了。JFR一般和JMC(Java Mission Control)协同工作。

JFR是一个基于事件的低开销的分析引擎,具有高性能的后端,可以以二进制格式编写事件,而JMC是一个GUI工具,用于检查JFR创建的数据文件。

这些工具最早是在BEA的JRockit JVM中出现的,最后被移植到了Oracle JDK。最开始JFR是商用版本,但是在JDK11的时候,JFR和JMC完全开源了,这意味着我们在非商用的情况下也可以使用了。

而在今天的JDK 14中,引入了一个新的JFR特性叫做JFR Event Streaming,我们将在本文中简要介绍。

先介绍一下JFR和JMC。

JFR

上面我们简单的介绍了一下JFR。JFR是JVM的调优工具,通过不停的收集JVM和java应用程序中的各种事件,从而为后续的JMC分析提供数据。

Event是由三部分组成的:时间戳,事件名和数据。同时JFR也会处理三种类型的Event:持续一段时间的Event,立刻触发的Event和抽样的Event。

为了保证性能的最新影响,在使用JFR的时候,请选择你需要的事件类型。

JFR从JVM中搜集到Event之后,会将其写入一个小的thread-local缓存中,然后刷新到一个全局的内存缓存中,最后将缓存中的数据写到磁盘中去。

或者你可以配置JFR不写到磁盘中去,但是这样缓存中只会保存部分events的信息。这也是为什么会有JDK14 JEP 349的原因。

开启JFR有很多种方式,这里我们关注下面两种:

  1. 添加命令行参数
-XX:StartFlightRecording:<options>

启动命令行参数的格式如上所述。

JFR可以获取超过一百种不同类型的元数据。如果要我们一个个来指定这些元数据,将会是一个非常大的功能。所以JDK已经为我们提供了两个默认的profile:default.jfc and profile.jfc。

其中 default.jfc 是默认的记录等级,对JVM性能影响不大,适合普通的,大部分应用程序。而profile.jfc包含了更多的细节,对性能影响会更多一些。

如果你不想使用默认的两个jfc文件,也可以按照你自己的需要来创建。

下面看一个更加完整的命令行参数:

-XX:StartFlightRecording:disk=true,filename=/tmp/customer.jfr,maxage=5h,settings=profile

上面的命令会创建一个最大age是5h的profile信息文件。

  1. 使用jcmd

命令行添加参数还是太麻烦了,如果我们想动态添加JFR,则可以使用jcmd命令。

jcmd <pid> JFR.start name=custProfile settings=default
jcmd <pid> JFR.dump filename=custProfile.jfr
jcmd <pid> JFR.stop

上面的命令在一个运行中的JVM中启动了JFR,并将统计结果dump到了文件中。

上面的custProfile.jfr是一个二进制文件,为了对其进行分析,我们需要和JFR配套的工具JMC。

JMC

JDK Mission Control 是一个用于对 Java 应用程序进行管理、监视、概要分析和故障排除的工具套件。

在JDK14中,JMC是独立于JDK单独发行的。我们可以下载之后进行安装。

我们先启动一个程序,用于做JFR的测试。

@Slf4j
public class ThreadTest {public static void main(String[] args) {ExecutorService executorService= Executors.newFixedThreadPool(10);Runnable runnable= ()->{while(true){log.info(Thread.currentThread().getName());try {Thread.sleep(500);} catch (InterruptedException e) {log.error(e.getMessage(),e);}}};for(int i=0; i<10; i++){executorService.submit(runnable);}}
}

很简单的一个程序,启动了10个线程,我们启动这个程序。

然后再去看看JMC的界面:

JMC非常强大,也有很多功能,具体的细节大家可以自己运行去体会。

JFR事件

JMC好用是好用,但是要一个一个的去监听JFR文件会很繁琐。接下来我们来介绍一下怎么采用写代码的方式来监听JFR事件。

如果我们想通过程序来获取“Class Loading Statistics"的信息,可以这样做。

上图的右侧是具体的信息,我们可以看到主要包含三个字段:开始时间,Loaded Class Count和 Unloaded Class Count。

我们的思路就是使用jdk.jfr.consumer.RecordingFile去读取生成的JFR文件,然后对文件中的数据进行解析。

相应代码如下:

@Slf4j
public class JFREvent {private static Predicate<RecordedEvent> testMaker(String s) {return e -> e.getEventType().getName().startsWith(s);}private static final Map<Predicate<RecordedEvent>,Function<RecordedEvent, Map<String, String>>> mappers =Map.of(testMaker("jdk.ClassLoadingStatistics"),ev -> Map.of("start", ""+ ev.getStartTime(),"Loaded Class Count",""+ ev.getLong("loadedClassCount"),"Unloaded Class Count", ""+ ev.getLong("unloadedClassCount")));@Testpublic void readJFRFile() throws IOException {RecordingFile recordingFile = new RecordingFile(Paths.get("/Users/flydean/flight_recording_1401comflydeaneventstreamThreadTest.jfr"));while (recordingFile.hasMoreEvents()) {var event = recordingFile.readEvent();if (event != null) {var details = convertEvent(event);if (details == null) {// details为空} else {// 打印目标log.info("{}",details);}}}}public Map<String, String> convertEvent(final RecordedEvent e) {for (var ent : mappers.entrySet()) {if (ent.getKey().test(e)) {return ent.getValue().apply(e);}}return null;}
}

注意,在convertEvent方法中,我们将从文件中读取的Event转换成了map对象。

在构建map时,我们先判断Event的名字是不是我们所需要的jdk.ClassLoadingStatistics,然后将Event中其他的字段进行转换。最后输出。

运行结果:

{start=2021-04-29T02:18:41.770618136Z, Loaded Class Count=2861, Unloaded Class Count=0}
...

可以看到输出结果和界面上面是一样的。

JFR事件流

讲了这么多,终于到我们今天要讲的内容了:JFR事件流。

上面的JFR事件中,我们需要去读取JFR文件,进行分析。但是文件是死的,人是活的,每次分析都需要先生成JFR文件简直是太复杂了。是个程序员都不能容忍。

在JFR事件流中,我们可以监听Event的变化,从而在程序中进行相应的处理。这样不需要生成JFR文件也可以监听事件变化。

    public static void main(String[] args) throws IOException, ParseException {//default or profile 两个默认的profiling configuration filesConfiguration config = Configuration.getConfiguration("default");try (var es = new RecordingStream(config)) {es.onEvent("jdk.GarbageCollection", System.out::println);es.onEvent("jdk.CPULoad", System.out::println);es.onEvent("jdk.JVMInformation", System.out::println);es.setMaxAge(Duration.ofSeconds(10));es.start();}}

看看上面的例子。我们通过Configuration.getConfiguration(“default”)获取到了默认的default配置。

然后通过构建了default的RecordingStream。通过onEvent方法,我们对相应的Event进行处理。

4_外部存储器API (孵化阶段)

通过一个API,以允许java程序安全有效的访问JAVA堆之外的外部存储(堆以外的外部存储空间)

目的:JEP 370旨在实现一种提供“通用性”,“安全性”和“确定性”的“外部存储器API”JEP还指出,此外部内存API旨在替代当前使用的方法( java.nio.ByteBuffer和sun.misc.Unsafe )。

许多java的库都能访问外部存储,例如 ignite ,mapDB , memcached以及netty的ByteBuffer API ,这样可以:

  • 避免垃圾回收相关成本和不可预测性
  • 跨多个进程共享内存
  • 通过将文件映射到内存中来序列化和反序列化内容

但是JAVAAPI本身没有提供一个令人满意的访问外部内存的解决方案

当java程序需要访问堆内存之外的外部存储是,通常有两种方式

  • java.nio.ByteBuffer ,:ByteBuffer 允许使用allcateDirect() 方法在堆内存之外分配内存空间

  • sum.misc.Unsafe : Unsafe 中的方法可以直接对内存地址进行操作

ByteBuffer有自己的限制. 首先是ByteBuffer的大小不能超过2G,其次是内存的释放依靠垃圾回收器,Unsafe的API在使用是不安全的,风险很高,可能会造成JVM崩溃.另外Unsafe本身是不被支持的API,并不推荐

JEP 370的“描述”部分引入了安全高效的API来访问外部外部内存地址,目前该API还是属于孵化阶段,相关API在jdk.incubator.foreign模块的jdk.incubator.foreign包中, 三个API分别是: MemorySegmentMemoryAddressMemoryLayoutMemorySegment用于对具有给定空间和时间范围的连续内存区域进行建模。 可以将MemoryAddress视为段内的偏移量。 最后, MemoryLayout是内存段内容的程序化描述。

5_非易失性映射字节缓冲区

JAVA14增加了一种文件映射模式,用于访问非易失性内存,非易失性内存能够持久保持数据,因此可以利用该特性来改进性能

JEP352 可以使用FileChannelAPI创建引用非易失性内存,(non-volatile memory) 的MappedByteBuffer实例,该JEP建议升级MappedByteBuffer以支持对非易失性存储器的访问,唯一需要的API更改是FileChannel客户端,以请求映射位于NVM的支持的文件系统,而不是常规的文件存储系统上的文件,对MappedByteBuffer API最新的更改意味着他支持允许直接内存更新所需要的所有行为,并提供更高级别的JAVA客户端库所需要的持久性保证,以实现持久性的数据类型

目标

NVM为引用程序程序员提供了在程序运行过程中创建和更新程序转台的机会,而减少了输出到持久性介质或者从持久性介质输入是的成本. 对于事务程序特别重要,在事务程序中,需要定期保持不确定状态以启用崩溃恢复.

现有的C库(例如Intel的libpmen),为c程序员提供了对集成NVM的高效访问,它们还一次基础来支持对各种持久性数据类型的简单管理.当前,由于频繁需要进行系统调用或者JNI来调用原始操作,从而确保内存更改是持久的,因此即使禁用JAVA的基础类库也很昂贵.同样的问题限制了高级库的使用.并且,由于C中提供的持久数据类型分配在无法从JAVA直接访问的内存中这一事实而加剧了这一问题.

该特性试图通过允许映射到ByteBuffer的NVM的有效写回来解决第一个问题. 由于java可以直接访问ByteBuffer映射内存,因此可以通过实现与C语言中提供的客户端库等效的客户端库来解决第二个问题,以管理不同持久数据类型存储.

初步变更

该JEP使用了JAVASE API的两个增强功能

  • 支持 Implementation-defined的映射模式
  • MappedByteBuffer::force方法指定范围

特定于JDK的API更改

  • 通过新模块中的公共API公开新的MApMode枚举值

一个公共扩展枚举ExtendedMapMode将添加到jdk.nio.mapmode程序包

package jdk.nio.mapmode;
public class ExtendedMapMode{private ExtendedMapMode(){}public static final MapMode READ_ONLY_SYNC=  ... ...}

在调用FileChannel::map方法创建映射到NVM设备文件上的只读或者写MappedByteBuffer时,可以使用上述的枚举值,如果这些标志在不支持NVM设备文件平台上传递,程序会抛出UnsupportedOperationException异常,在受支持的平台上,及当目标FileChannel实例是通过NVM设备打开的派生文件是,才能传递这些参数,在任何情况下,都会抛出IOException;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/319214.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-8.2-链接脚本

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

3.9设计模式——Strategy 策略模式(行为型)

意图 定义一系列的算法&#xff0c;把它们一个个封装起来&#xff0c;并且使他们可以相互替换此模式使得算法可以独立于使用它们的客户而变化 结构 Strategy&#xff08;策略&#xff09;定义所有支持的算法的公共入口。Context使用这个接口来调用某ConcreteStrategy定义的方…

实验14 MVC

二、实验项目内容&#xff08;实验题目&#xff09; 编写代码&#xff0c;掌握MVC的用法。【参考课本 例1 】 三、源代码以及执行结果截图&#xff1a; example7_1.jsp&#xff1a; <% page contentType"text/html" %> <% page pageEncoding "ut…

【信息收集-基于字典爆破敏感目录--御剑/dirsearch

两个工具都是内置字典来对于目录进行爆破的&#xff0c;这是信息收集的一部分&#xff0c;若能在列举出的目录中找到有价值的信息能为后续渗透做准备。 御剑比较简便 dirsearch需要集成python3.x环境&#xff0c;但是可选的命令更多。两者爆破的结果不一定相同&#xff0c;可以…

怎样建设网站

建设一个网站需要经过一系列的步骤和技术&#xff0c;以下是一个简单的指导&#xff1a; 1. 确定网站目的&#xff1a;首先要确定网站的目的和目标。是为了促销产品&#xff1f;提供信息&#xff1f;还是为了社交交流&#xff1f;确定网站目的可以帮助你更好地规划网站的结构和…

【深度学习】位置编码

一、引言 Self-Attention并行的计算方式未考虑输入特征间的位置关系&#xff0c;这对NLP来说是不可接受的&#xff0c;毕竟一个句子中每个单词都有着明显的顺序关系。Transformer没有RNN、LSTM那样的顺序结构&#xff0c;所以Transformer在提出Self-Attention的同时提出了Posi…

RKNN Toolkit2 工具的使用

RKNN Toolkit2 是由瑞芯微电子 (Rockchip) 开发的一套用于深度学习模型优化和推理的工具。它主要面向在瑞芯微SoC上进行AI应用开发&#xff0c;但也可以用于PC平台进行模型的转换、量化、推理等操作。它支持将多种深度学习框架的模型&#xff08;如Caffe, TensorFlow, PyTorch等…

Linux下软硬链接和动静态库制作详解

目录 前言 软硬链接 概念 软链接的创建 硬链接的创建 软硬链接的本质区别 理解软链接 理解硬链接 小结 动静态库 概念 动静态库的制作 静态库的制作 动态库的制作 前言 本文涉及到inode和地址空间等相关概念&#xff0c;不知道的小伙伴可以先阅读以下两篇文章…

网络安全是智能汽车下一个要卷的方向?

2024年一季度&#xff0c;中国汽车市场延续了2023年的风格&#xff0c;核心就是「卷」。 2023年&#xff0c;我国汽车市场爆发「最强价格战」&#xff0c;燃油车的市场空间不断被挤压&#xff0c;如今只剩下最后一口气。近日乘联会发布4月1-14日最新数据&#xff0c;新能源&am…

【前端】VUE项目创建

在所需文件夹中打开cmd命令行窗口&#xff0c;输入vue ui 进入web可视化界面选择创建新项目 根据需求依次完成下列选择&#xff0c;下列是参考配置&#xff0c;完成后点击创建项目即可 最终显示完成

CUDA和显卡驱动

1.安装显卡驱动 https://www.nvidia.com/download/index.aspx?langen-us 由于我的显卡是RTX4060&#xff0c;因此先选择RTX40系列&#xff0c;然后选择RTX4060&#xff0c;进行安装 2.查看显卡对应的CUDA CUDA安装地址&#xff1a;https://developer.nvidia.com/cuda-toolk…

应用分层和企业规范

目录 一、应用分层 1、介绍 &#xff08;1&#xff09;为什么需要应用分层&#xff1f; &#xff08;2&#xff09;如何分层&#xff1f;&#xff08;三层架构&#xff09; MVC 和 三层架构的区别和联系 高内聚&#xff1a; 低耦合&#xff1a; 2、代码重构 controlle…

【 书生·浦语大模型实战营】学习笔记(六):Lagent AgentLego 智能体应用搭建

&#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料&#xff0c;配有全面而有深度的专栏内容&#xff0c;包括不限于 前沿论文解读、…

相机知识的补充

一&#xff1a;镜头 1.1MP的概念 相机中MP的意思是指百万像素。MP是mega pixel的缩写。mega意为一百万&#xff0c;mega pixel 指意为100万像素。“像素”是相机感光器件上的感光最小单位。就像是光学相机的感光胶片的银粒一样&#xff0c;记忆在数码相机的“胶片”&#xff…

spring boot运行过程中动态加载Controller

1.被加载的jar代码 package com.dl;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication public class App {public static void main(String[] args) {SpringApplication.run(A…

C++函数模板

简介&#xff1a;C函数模板的作用就是按照程序员的要求生成想要的函数对象。本质上是一种函数声明&#xff0c;在程序运行时依靠指定的参数类型由编译器临时生成函数对象。 1、auto自动类型推导 auto关键字可以取代变量声明时的函数类型&#xff0c;其实实际上会由编译器帮你把…

智能私信神器,转化率飙升的秘密!

在当今信息爆炸的时代&#xff0c;企业和商家面临着巨大的竞争压力&#xff0c;如何有效地吸引潜在客户、提高客户转化率成为摆在每一位市场营销人员面前的难题。随着人工智能技术的不断发展&#xff0c;智能私信软件应运而生&#xff0c;为企业提供了一个高效、便捷的解决方案…

前端发起网络请求的几种常见方式(XMLHttpRequest、FetchApi、jQueryAjax、Axios)

摘要 前端发起网络请求的几种常见方式包括&#xff1a; XMLHttpRequest (XHR)&#xff1a; 这是最传统和最常见的方式之一。它允许客户端与服务器进行异步通信。XHR API 提供了一个在后台发送 HTTP 请求和接收响应的机制&#xff0c;使得页面能够在不刷新的情况下更新部分内容…

HSDB使用教程

HSDB&#xff1a;Hostspot Debugger&#xff0c;JVM内置的工具&#xff0c;用于深入分析JVM运行时的内部状态 启动HSDB java -cp D:/tools/jdk-1.8/lib/sa-jdi.jar sun.jvm.hotspot.HSDB 获取进程id jps 连接到指定进程 查找类 通过查询查找对象 输入查询语句 select d from …

扫雷实现详解【递归展开+首次必展开+标记雷+取消标记雷】

扫雷 一.扫雷设计思路二.扫雷代码逐步实现1.创建游戏菜单2.初始化棋盘3.打印棋盘4.随机布置雷5.统计周围雷的个数6.递归展开棋盘7.标记雷8.删除雷的标记9.保证第一次排雷的安全性棋盘必定展开10.排查雷11.判断输赢 三.扫雷总代码四.截图 一.扫雷设计思路 1.创建游戏菜单。  2.…