文章目录
- 1.jShell
- 2.局部变量
- 3.集合中一些增强的Api
- 4.流的一些增强的Api
- 5.字符串的新api
- 6.Optional加强
- 7.IO文件的API改进
- 8.Http客户端API
- 9.更简化的编译运行程序
- 10.EpsilonGC垃圾回收器
- 11.ZGC
- 12.完全支持Linux容器(包括Docker)
- 13.支持G1上的并行完全垃圾收集。
- 14.JEP 331 : Low-Overhead Heap Profiling免费的低耗能飞行记录仪和堆分析仪。
- 15.JEP 329 : 实现RFC7539中指定的ChaCha20和Poly1305两种加密算法, 代替RC4
- 16.Java Flight Recorder
1.jShell
就像一个客户端 目录在
通过命令打开
相当于进入了main方法 , 简单使用一下
jshell> var name = "xl";
name ==> "xl"
jshell> name
name ==> "xl"
1.快捷提醒与补全
按tab键进行提醒和补全
jshell> System.out.print
print( printf( println(
jshell> System.out.print
2.help命令
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
| 重新评估以前输入片段的方法的说明
2.局部变量
var
关键字来定义,
var val = "123";
等同于 String val = "123";
他是根据右边的实际值,去推断左边的值的数据类型
注意:
- 1.类的属性不允许使用var来定义
- 2.必须赋予初始值
例子:
jshell> Consumer<String> consumer = (var t) -> System.out.println(t.toUpperCase());
consumer ==> $Lambda$21/0x00000008000b6040@59ec2012
jshell> consumer.accept("dasdFSDFSD")
DASDFSDFSD
3.集合中一些增强的Api
of的使用
通过of去创建一个list集合
List<String> list = List.of("aa", "bb", "cc", "DD");
System.out.println(list);
运行输出
但 如果这样做呢?
就会报这个错误
我们来测试下 快速把数据变成集合的方法
发现也会报相同的错误
看asList
的源码发现,这个ArrayList
他继承了一个抽象的类,并不是java.util.ArrayList
!
接着,发现里面并没有add方法
而抽象类里面有add方法
来看一下add方法
直接抛了一个异常
所以,他是一个不可添加的集合
那么我们进of
方法看一下源码
ListN方法源码
static final class ListN<E> extends AbstractImmutableList<E>
implements Serializable {
static final List<?> EMPTY_LIST = new ListN<>();
@Stable
private final E[] elements;
@SafeVarargs
ListN(E... input) {
// copy and check manually to avoid TOCTOU
@SuppressWarnings("unchecked")
E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
for (int i = 0; i < input.length; i++) {
tmp[i] = Objects.requireNonNull(input[i]);
}
elements = tmp;
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public int size() {
return elements.length;
}
@Override
public E get(int index) {
return elements[index];
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new InvalidObjectException("not serial proxy");
}
private Object writeReplace() {
return new CollSer(CollSer.IMM_LIST, elements);
}
}
发现它还是个子类
发现它的父类中,有add方法
不过很遗憾,add方法里面也抛了这个异常
实战场景:
源码:
public static LocalDate of(int year, int month, int dayOfMonth) {
YEAR.checkValidValue(year);
MONTH_OF_YEAR.checkValidValue(month);
DAY_OF_MONTH.checkValidValue(dayOfMonth);
return create(year, month, dayOfMonth);
}
set也是支持的
Stream也是支持的
4.流的一些增强的Api
1.跟简便的消费流的写法
2.流中没有数据 和 null
1.流中没有数据
如果这样写,会发生什么呢?
会发现,他什么都没输出
2.流中有null
报错了!
跟踪源码发现:
发现了会造成null的原因。
所以不可以传入null!
那么该这么传进去呢?
其实就是进行了一个判断
3.获取和删除方法
1.获取方法 takeWhile
发现他是一次性
就停止的,如果1%2
不等于0的话,就取出来,后面的就直接break
掉
那么takeWhile方法就是从流中获取判定器为true的元素,一旦为false就终止。
注意:一旦处理过的流,就不能再继续使用!
2.删除方法 dropWhile
4.流的迭代
1.无限流
他会无限的循环 -1,为什么呢?
2.有限流
中间的参数,即是判断,如果符合,就停止迭代
5.字符串的新api
判断字符串是否为空白
System.out.println("".isBlank()); //true
去除首位空格
System.out.println(" csdnCxl ".strip()); //csdnCxl
去除尾部空格
System.out.println(" csdnCxl ".stripTrailing()); // csdnCxl
去除首部空格
System.out.println(" csdnCxl ".stripLeading()); //csdnCxl
复制字符串
System.out.println("nb".repeat(3)); //nbnbnb
行数统计
System.out.println("a\nb\nc\n".lines().count());//3
整体test
6.Optional加强
创建一个optional对象
解决空指针问题
orElse 如果内部引用为null,则返回参数中的引用
@Test
public void test1(){
//orElse 如果内部引用为null,则返回参数中的引用
// Optional<String> optObj = Optional.ofNullable(null);
// String s = optObj.orElse("123"); //123
// String s = optObj.orElse(null); //null
Optional<String> optObj = Optional.ofNullable("default");
String s = optObj.orElse(null);
System.out.println(s);// default
}
orElseThrow 如果内部引用为null,则抛出异常
源码:
/**
* 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;
}
orElseGet 如果为空,通过中继器获取
对比源码即可发现
/**
* If a value is present, returns the value, otherwise returns
* {@code other}.
*
* @param other the value to be returned, if no value is present.
* May be {@code null}.
* @return the value, if present, otherwise {@code other}
*/
public T orElse(T other) {
return value != null ? value : other;
}
/**
* 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();
}
两者的相差并不大
7.IO文件的API改进
复制文件 transferTo
@Test
public void test1() throws Exception{
ClassLoader classLoader = this.getClass().getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("hello");
try(var os = new FileOutputStream("myfile")) {
resourceAsStream.transferTo(os);
}
resourceAsStream.close();
}
8.Http客户端API
public class HttpApiTest {
/**
* 同步请求
* @throws Exception
*/
@Test
public void test1() throws Exception{
HttpClient httpClient = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create("www.baidu.com")).build();
BodyHandler<String> responseBodyHandler = BodyHandlers.ofString(); // res处理器
HttpResponse<String> send = httpClient.send(request, responseBodyHandler);
System.out.println(send.body());
}
/**
* 异步
* @throws Exception
*/
@Test
public void test2() throws Exception{
HttpClient httpClient = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create("www.baidu.com")).build();
BodyHandler<String> responseBodyHandler = BodyHandlers.ofString(); // res处理器
CompletableFuture<HttpResponse<String>> httpResponseCompletableFuture = httpClient.sendAsync(request, responseBodyHandler);
System.out.println(httpResponseCompletableFuture.get().body());
}
}
9.更简化的编译运行程序
JEP 330:执行源文件中的第一个类,并且不可以使用别的源文件中的自定义类
之前我们需要javac hello.java
java hello
而在java11中,直接使用java hello.java
即可
发现成功输出!
10.EpsilonGC垃圾回收器
A NoOp Garbage
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("程序结束");
}
}
如果使用选项-XX:+UseEpsilonGC, 程序很快就因为堆空间不足而退出
使用这个选项的原因 :
提供完全被动的GC实现, 具有有限的分配限制和尽可能低的延迟开销,但代价是内存占用和内存吞吐量.
众所周知, java实现可广泛选择高度可配置的GC实现. 各种可用的收集器最终满足不同的需求, 即使它们的可配置性使它们的功能相交. 有时更容易维护单独的实现, 而不是在现有GC实现上堆积另一个配置选项.
主要用途如下 :
性能测试(它可以帮助过滤掉GC引起的性能假象)
内存压力测试(例如,知道测试用例 应该分配不超过1GB的内存, 我们可以使用-Xmx1g –XX:+UseEpsilonGC, 如果程序有问题, 则程序会崩溃)
非常短的JOB任务(对象这种任务, 接受GC清理堆那都是浪费空间)
VM接口测试
Last-drop 延迟&吞吐改进
11.ZGC
ZGC, 这应该是JDK11最为瞩目的特性, 没有之一. 但是后面带了Experimental, 说明这还不建议用到生产环境.
ZGC, A Scalable Low-Latency Garbage Collector(Experimental)
ZGC, 这应该是JDK11最为瞩目的特性, 没有之一. 但是后面带了Experimental, 说明这还不建议用到生产环境.
GC暂停时间不会超过10ms
既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)
和G1相比, 应用吞吐能力不会下降超过15%
为未来的GC功能和利用colord指针以及Load barriers优化奠定基础
初始只支持64位系统
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参数来解锁这个特性
12.完全支持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"
最大堆大小为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
设置可用的CPU
默认情况下,每个容器对主机CPU周期的访问是无限的。可以设置各种约束来限制给定容器对主机CPU周期的访问。Java 10可以识别这些限制:
docker container run -it --cpus 2 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2
分配给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
cpuset约束设置了哪些CPU允许在Java 10中执行。
docker run -it --cpuset-cpus="1,2,3" openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 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来处理工作负载。
13.支持G1上的并行完全垃圾收集。
对于 G1 GC,相比于 JDK 8,升级到 JDK 11 即可免费享受到:并行的 Full GC,快速的 CardTable 扫描,自适应的堆占用比例调整(IHOP),在并发标记阶段的类型卸载等等。这些都是针对 G1 的不断增强,其中串行 Full GC 等甚至是曾经被广泛诟病的短板,你会发现 GC 配置和调优在 JDK11 中越来越方便。
14.JEP 331 : Low-Overhead Heap Profiling免费的低耗能飞行记录仪和堆分析仪。
通过JVMTI的SampledObjectAlloc回调提供了一个开销低的heap分析方式
提供一个低开销的, 为了排错java应用问题, 以及JVM问题的数据收集框架, 希望达到的目标如下 :
提供用于生产和消费数据作为事件的API
提供缓存机制和二进制数据格式
允许事件配置和事件过滤
提供OS,JVM和JDK库的事件
15.JEP 329 : 实现RFC7539中指定的ChaCha20和Poly1305两种加密算法, 代替RC4
实现 RFC 7539的ChaCha20 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()
16.Java Flight Recorder
Flight Recorder源自飞机的黑盒子
Flight Recorder以前是商业版的特性,在java11当中开源出来,它可以导出事件到文件中,之后可以用Java Mission Control来分析。可以在应用启动时配置java -XX:StartFlightRecording,或者在应用启动之后,使用jcmd来录制,比如
$ jcmd JFR.start
$ jcmd JFR.dump filename=recording.jfr
$ jcmd JFR.stop
是 Oracle 刚刚开源的强大特性。我们知道在生产系统进行不同角度的 Profiling,有各种工具、框架,但是能力范围、可靠性、开销等,大都差强人意,要么能力不全面,要么开销太大,甚至不可靠可能导致 Java 应用进程宕机。
而 JFR 是一套集成进入 JDK、JVM 内部的事件机制框架,通过良好架构和设计的框架,硬件层面的极致优化,生产环境的广泛验证,它可以做到极致的可靠和低开销。在 SPECjbb2015 等基准测试中,JFR 的性能开销最大不超过 1%,所以,工程师可以基本没有心理负担地在大规模分布式的生产系统使用,这意味着,我们既可以随时主动开启 JFR 进行特定诊断,也可以让系统长期运行 JFR,用以在复杂环境中进行“After-the-fact”分析。还需要苦恼重现随机问题吗?JFR 让问题简化了很多。
在保证低开销的基础上,JFR 提供的能力也令人眼前一亮,例如:我们无需 BCI 就可以进行 Object Allocation Profiling,终于不用担心 BTrace 之类把进程搞挂了。对锁竞争、阻塞、延迟,JVM GC、SafePoint 等领域,进行非常细粒度分析。甚至深入 JIT Compiler 内部,全面把握热点方法、内联、逆优化等等。JFR 提供了标准的 Java、C++ 等扩展 API,可以与各种层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供 All-in-One 解决方案。而这一切都是内建在 JDK 和 JVM 内部的,并不需要额外的依赖,开箱即用。