小师妹学JVM之:JDK14中JVM的性能优化
简介
上一篇文章我们讲到了JVM为了提升解释的性能,引入了JIT编译器,今天我们再来从整体的角度,带小师妹看看JDK14中的JVM有哪些优化的方面,并且能够从中间得到那些启发。
更多精彩内容且看:
- 区块链从入门到放弃系列教程-涵盖密码学,超级账本,以太坊,Libra,比特币等持续更新
- Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
- Spring 5.X系列教程:满足你对Spring5的一切想象-持续更新
- java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程
String压缩
小师妹:F师兄,上次你给我讲的JIT真的是受益匪浅,原来JVM中还有这么多不为人知的小故事。不知道除了JIT之外,JVM还有没有其他的性能提升的姿势呢?
姿势当然有很多种,先讲一下之前提到过的,在JDK9中引入的字符串压缩。
在JDK9之前,String的底层存储结构是char[],一个char需要占用两个字节的存储单位。
因为大部分的String都是以Latin-1字符编码来表示的,只需要一个字节存储就够了,两个字节完全是浪费。
于是在JDK9之后,字符串的底层存储变成了byte[]。
目前String支持两种编码格式LATIN1和UTF16。
LATIN1需要用一个字节来存储。而UTF16需要使用2个字节或者4个字节来存储。
在JDK9中,字符串压缩是默认开启的。你可以使用
-XX:-CompactStrings
来控制它。
分层编译(Tiered Compilation)
为了提升JIT的编译效率,并且满足不同层次的编译需求,引入了分层编译的概念。
大概来说分层编译可以分为三层:
- 第一层就是禁用C1和C2编译器,这个时候没有JIT进行。
- 第二层就是只开启C1编译器,因为C1编译器只会进行一些简单的JIT优化,所以这个可以应对常规情况。
- 第三层就是同时开启C1和C2编译器。
在JDK7中,你可以使用下面的命令来开启分层编译:
-XX:+TieredCompilation
而在JDK8之后,恭喜你,分层编译已经是默认的选项了,不用再手动开启。
Code Cache分层
Code Cache就是用来存储编译过的机器码的内存空间。也就说JIT编译产生的机器码,都是存放在Code Cache中的。
Code Cache是以单个heap形式组织起来的连续的内存空间。
如果只是用一个code heap,或多或少的就会引起性能问题。为了提升code cache的利用效率,JVM引入了Code Cache分层技术。
分层技术是什么意思呢?
就是把不同类型的机器码分门别类的放好,优点嘛就是方便JVM扫描查找,减少了缓存的碎片,从而提升了效率。
下面是Code Cache的三种分层:

新的JIT编译器Graal
之前的文章我们介绍JIT编译器,讲的是JIT编译器是用C/C++来编写的。
而新版的Graal JIT编译器则是用java来编写的。对的,你没看错,使用java编写的JIT编译器。
有没有一种鸡生蛋,蛋生鸡的感觉?不过,这都不重要,重要的是Graal真的可以提升JIT的编译性能。
Graal是和JDK一起发行的,作为一个内部的模块:jdk.internal.vm.compiler。
Graal和JVM是通过JVMCI(JVM Compiler Interface)来进行通信的。其中JVMCI也是一个内部的模块:jdk.internal.vm.ci。
注意,Graal只在Linux-64版的JVM中支持,你需要使用 -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler 来开启Graal特性。
前置编译
我们知道在JIT中,通常为了找到热点代码,JVM是需要等待代码执行一定的时间之后,才开始进行本地代码的编译。这样做的缺点就是需要比较长的时间。
同样的,如果是重复的代码,没有被编译成为机器码,那么对性能就会有影响。
而AOT(Ahead-of-time)就厉害了,看名字就知道是提前编译的意思,根本就不需要等待,而是在JVM启动之前就开始编译了。
AOT提供了一个java tool,名字叫做jaotc。显示jaotc的命令格式:
jaotc <options> <list of classes or jar files>
jaotc <options> <--module name>
比如,我们可以这样提前编译AOT库,以供在后面的JVM中使用:
jaotc --output libHelloWorld.so HelloWorld.class
jaotc --output libjava.base.so --module java.base
上面代码提前编译了HelloWorld和它的依赖module java.base。
然后我们可以在启动HelloWorld的时候,指定对应的lib:
java -XX:AOTLibrary=./libHelloWorld.so,./libjava.base.so HelloWorld
这样在JVM启动的时候,就回去找相应的AOTLibrary。
注意,AOT是一个 Linux-x64上面的体验功能。
压缩对象指针
对象指针用来指向一个对象,表示对该对象的引用。通常来说在64位机子上面,一个指针占用64位,也就是8个字节。而在32位机子上面,一个指针占用32位,也就是4个字节。
实时上,在应用程序中,这种对象的指针是非常非常多的,从而导致如果同样一个程序,在32位机子上面运行和在64位机子上面运行占用的内存是完全不同的。64位机子内存使用可能是32位机子的1.5倍。
而压缩对象指针,就是指把64位的指针压缩到32位。
怎么压缩呢?64位机子的对象地址仍然是64位的。压缩过的32位存的只是相对于heap base address的位移。
我们使用64位的heap base地址+ 32位的地址位移量,就得到了实际的64位heap地址。
对象指针压缩在Java SE 6u23 默认开启。在此之前,可以使用-XX:+UseCompressedOops来开启。
Zero-Based 压缩指针
刚刚讲到了压缩过的32位地址是基于64位的heap base地址的。而在Zero-Based 压缩指针中,64位的heap base地址是重新分配的虚拟地址0。这样就可以不用存储64位的heap base地址了。
Escape analysis逃逸分析
最后,要讲的是逃逸分析。什么叫逃逸分析呢?简单点讲就是分析这个线程中的对象,有没有可能会被其他对象或者线程所访问,如果有的话,那么这个对象应该在Heap中分配,这样才能让对其他的对象可见。
如果没有其他的对象访问,那么完全可以在stack中分配这个对象,栈上分配肯定比堆上分配要快,因为不用考虑同步的问题。
我们举个例子:
public static void main(String[] args) {
example();
}
public static void example() {
Foo foo = new Foo(); //alloc
Bar bar = new Bar(); //alloc
bar.setFoo(foo);
}
}
class Foo {}
class Bar {
private Foo foo;
public void setFoo(Foo foo) {
this.foo = foo;
}
}
上面的例子中,setFoo引用了foo对象,如果bar对象是在heap中分配的话,那么引用的foo对象就逃逸了,也需要被分配在heap空间中。
但是因为bar和foo对象都只是在example方法中调用的,所以,JVM可以分析出来没有其他的对象需要引用他们,那么直接在example的方法栈中分配这两个对象即可。
逃逸分析还有一个作用就是lock coarsening。
为了在多线程环境中保证资源的有序访问,JVM引入了锁的概念,虽然锁可以保证多线程的有序执行,但是如果实在单线程环境中呢?是不是还需要一直使用锁呢?
比如下面的例子:
public String getNames() {
Vector<String> v = new Vector<>();
v.add("Me");
v.add("You");
v.add("Her");
return v.toString();
}
Vector是一个同步对象,如果是在单线程环境中,这个同步锁是没有意义的,因此在JDK6之后,锁只在被需要的时候才会使用。
这样就能提升程序的执行效率。
本文作者:flydean程序那些事
本文链接:http://www.flydean.com/jvm-performance-enhancements/
本文来源:flydean的博客
欢迎关注我的公众号:程序那些事,更多精彩等着您!
小师妹学JVM之:JDK14中JVM的性能优化的更多相关文章
- 小师妹学JavaIO之:NIO中那些奇怪的Buffer
目录 简介 Buffer的分类 Big Endian 和 Little Endian aligned内存对齐 总结 简介 妖魔鬼怪快快显形,今天F师兄帮助小师妹来斩妖除魔啦,什么BufferB,Buf ...
- 小师妹学JavaIO之:NIO中Channel的妙用
目录 简介 Channel的分类 FileChannel Selector和Channel DatagramChannel SocketChannel ServerSocketChannel Asyn ...
- 小师妹学JVM之:逃逸分析和TLAB
目录 简介 逃逸分析和栈上分配 TLAB简介 TLAB详解 设置TLAB空间的大小 TLAB中大对象的分配 TLAB空间中的浪费 总结 简介 逃逸分析我们在JDK14中JVM的性能优化一文中已经讲过了 ...
- 小师妹学JVM之:JVM的架构和执行过程
目录 简介 JVM是一种标准 java程序的执行顺序 JVM的架构 类加载系统 运行时数据区域 执行引擎 总结 简介 JVM也叫Java Virtual Machine,它是java程序运行的基础,负 ...
- 小师妹学IO系列文章集合-附PDF下载
目录 第一章 IO的本质 IO的本质 DMA和虚拟地址空间 IO的分类 IO和NIO的区别 总结 第二章 try with和它的底层原理 简介 IO关闭的问题 使用try with resource ...
- 小师妹学JavaIO之:文件系统和WatchService
目录 简介 监控的痛点 WatchService和文件系统 WatchSerice的使用和实现本质 总结 简介 小师妹这次遇到了监控文件变化的问题,F师兄给小师妹介绍了JDK7 nio中引入的Watc ...
- vue中关于v-for性能优化---track-by属性
vue中关于v-for性能优化---track-by属性 最近看了一些react,angular,Vue三者的对比文章,对比来说Vue比较突出的是轻量级与易上手. 对比Vue与angular,Vue有 ...
- Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析
Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析 说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们 ...
- 小师妹学JVM之:JIT中的PrintAssembly
目录 简介 使用PrintAssembly 输出过滤 总结 简介 想不想了解JVM最最底层的运行机制?想不想从本质上理解java代码的执行过程?想不想对你的代码进行进一步的优化和性能提升? 如果你的回 ...
随机推荐
- MyBatis的使用增删改查(两种分页查询)
文件目录 写一下每个文件的代码 UserDao.java package cn.zys.dao; import java.io.IOException; import java.util.List; ...
- 那些面试官必问的JAVA多线程和并发面试题及回答
Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环 ...
- 小智的旅行(Bridge)51nod 提高组试题
luogu AC传送门(官方数据) 题目描述 小智最喜欢旅行了,这次,小智来到了一个岛屿众多的地方,有N座岛屿,编号为0到N-1,岛屿之间 由一些桥连接,可以从桥的任意一端到另一端,由于岛屿可能比较大 ...
- CPU-如何开始在新的CPU上编程
https://mp.weixin.qq.com/s/rNXDPR53m--XuvJLE1CDvA 新在哪里?从未接触过.比如之前一直在x86.ARM上写程序,C比较多,汇编也调过.MIPS可能零 ...
- jchdl - GSL实例 - Div
因为对除法研究不深,这里略去不表. 有兴趣可以参考链接: https://github.com/wjcdx/jchdl/blob/master/src/org/jchdl/model/gsl/op ...
- Java并发编程 (六) 线程安全策略
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.不可变对象-1 有一种安全的发布对象,即不可变对象. 1.不可变对象需要满足的条件 ① 对象创建以后 ...
- (二)用less+gulp+requireJs 搭建项目(gulp)
gulp是自动化构建工具,基于node,需要安装node,如果你不了解node也没关系,先跟着来一遍再去了解node也不迟~ 首先去node官网下载安装包 1.新建项目文件夹 在目录下shift+右键 ...
- Java实现 LeetCode 653 两数之和 IV - 输入 BST(递归,找差值)
653. 两数之和 IV - 输入 BST 给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true. 案例 1: 输入: 5 / \ 3 6 / ...
- Java实现 LeetCode 427 建立四叉树
427. 建立四叉树 我们想要使用一棵四叉树来储存一个 N x N 的布尔值网络.网络中每一格的值只会是真或假.树的根结点代表整个网络.对于每个结点, 它将被分等成四个孩子结点直到这个区域内的值都是相 ...
- Java实现 蓝桥杯VIP 算法训练 会议中心
算法训练 会议中心 时间限制:2.0s 内存限制:512.0MB 会议中心 Siruseri政府建造了一座新的会议中心.许多公司对租借会议中心的会堂很感兴趣,他们希望能够在里面举行会议. 对于一个客户 ...