花半秒钟就能看透事物本质的人,和花一辈子都看不清事物本质的人,注定是截然不同的命运 --教父

个人公众号:月伴飞鱼,欢迎关注

之前说好的这期讲解并发工具类,不过ReentrantLock源码还没肝完,理由嘛,太忙了,身体不舒服,脑袋没货,睡眠不足,剧还没追完........

但说好的每周一篇干货,不能停,今天就先介绍一篇JVM相关知识

我们知道Java虚拟机栈是线程私有的,每个线程对应一个栈,每个线程在执行一个方法时会创建一个对应的栈帧,栈帧负责存储局部变量变量表、操作数栈、动态链接和方法返回地址等信息,每个方法的调用过程,相当于栈帧在Java栈的入栈和出栈过程

但是栈帧的创建是需要耗费资源的,尤其是对于 Java 中常见的 getter、setter 方法来说,这些代码通常只有一行,每次都创建栈帧的话就太浪费了。

另外,Java 虚拟机栈对代码的执行,采用的是字节码解释执行的方式,考虑到下面这段代码,变量 a 声明之后,就再也不被使用,要是按照字节码指令解释执行的话,就要做很多无用功。public class A{

int attr = 0;

public void test(){

int a = attr;

System.out.println("月伴飞鱼");

}

}

执行如下命令:javap -v A

可以看到这段代码的字节码指令

我们能够看到 aload_0,getfield ,istore_1 这三个无用的字节码指令操作。aload_0 从局部变量0中装载引用类型值,getfield 从对象中获取字段,istore_1 将int类型值存入局部变量1

另外,我们知道垃圾回收器回收的目标区域主要是堆,堆上创建的对象越多,GC 的压力就越大。要是能把一些变量,直接在栈上分配,那 GC 的压力就会小一些。

其实,我们说的这几个优化的可能性,JVM 已经通过JIT 编译器(Just In Time Compiler)去做了,JIT 最主要的目标是把解释执行变成编译执行。

为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,这就是 JIT 编译器的功能。

如上图,JVM 会将调用次数很高,或者在 for 循环里频繁被使用的代码,编译成机器码,然后缓存起来,下次调用相同方法的时候,就可以直接使用。

那 JIT 编译都有哪些手段呢?接下来我们详细介绍。

方法内联

方法内联它会把一些短小的方法体,直接纳入目标方法的作用范围之内,就像是直接在代码块中追加代码。这样,就少了一次方法调用,执行速度就能够得到提升,这就是方法内联的概念。

可以使用 -XX:-Inline 参数来禁用方法内联,如果想要更细粒度的控制,可以使用 CompileCommand 参数,例如:-XX:CompileCommand=exclude,java/lang/String.indexOf

在 JDK 的源码里,也有很多被 @ForceInline注解的方法,这些方法,会在执行的时候被强制进行内联;而被@DontInline注解的方法,则始终不会被内联。

JIT 编译之后的二进制代码,是放在 Code Cache 区域里的。这个区域的大小是固定的,而且一旦启动无法扩容。如果 Code Cache 满了,JVM 并不会报错,但会停止编译。所以编译执行就会退化为解释执行,性能就会降低。不仅如此,JIT 编译器会一直尝试去优化你的代码,造成 CPU 占用上升。

通过参数 -XX:ReservedCodeCacheSize 可以指定 Code Cache 区域的大小,如果你通过监控发现空间达到了上限,就要适当的增加它的大小。

分层编译

HotSpot 虚拟机包含多个即时编译器,有 C1,C2 和 Graal,JDK8 以后采用的是分层编译的模式。

JMV使用额外线程进行即时编译,可以不用阻塞解释执行的逻辑。JIT 通常会在触发之后就在后台运行,编译完成之后就将相应的字节码替换为编译后的代码。

JIT 编译方式有两种:一种是编译方法,另一种是编译循环。

具体介绍下几个编译器

C1 编译器

C1 编译器是一个简单快速的编译器,主要的关注点在于局部性的优化,适用于执行时间较短或对启动性能有要求的程序,也称为Client Compiler,例如,GUI 应用对界面启动速度就有一定要求。

C2 编译器

C2 编译器是为长期运行的服务器端应用程序做性能调优的编译器,适用于执行时间较长或对峰值性能有要求的程序,也称为Server Compiler,例如,服务器上长期运行的 Java 应用对稳定运行就有一定的要求。

在 Java7 之前,需要根据程序的特性来选择对应的 JIT,虚拟机默认采用解释器和其中一个编译器配合工作。

分层编译

Java7 引入了分层编译,这种方式综合了 C1 的启动性能优势和 C2 的峰值性能优势,我们也可以通过参数 -client或者-server 强制指定虚拟机的即时编译模式。

通常情况下,C2 的执行效率比 C1 高出30%以上。

注意:在 Java8 中,默认开启分层编译,-client 和 -server 的设置已经是无效的了。

如果只想开启 C2,可以关闭分层编译(-XX:-TieredCompilation),如果只想用 C1,可以在打开分层编译的同时,使用参数:-XX:TieredStopAtLevel=1。

我们可以通过 java -version命令行可以直接查看到当前系统使用的编译模式:C:\Users\Administrator>java -version

java version "1.8.0_45"

Java(TM) SE Runtime Environment (build 1.8.0_45-b14)

Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

mixed mode代表是默认的混合编译模式,除了这种模式外,我们还可以使用-Xint参数强制虚拟机运行于只有解释器的编译模式下,这时 JIT 完全不介入工作;也可以使用参数-Xcomp强制虚拟机运行于只有 JIT 的编译模式下

逃逸分析

下面着重讲解一下逃逸分析,这个知识点在面试的时候经常会被问到。

有这样一个问题:我们常说的对象,除了基本数据类型,一定是在堆上分配的吗?

答案是否定的,通过逃逸分析,JVM 能够分析出一个新的对象的使用范围,从而决定是否要将这个对象分配到堆上。逃逸分析现在是 JVM 的默认行为,可以通过参数-XX:-DoEscapeAnalysis 关掉它。

那什么样的对象算是逃逸的呢?可以看一下下面的两种典型情况。

如代码所示,对象被赋值给成员变量或者静态变量,可能被外部使用,变量就发生了逃逸。public class EscapeAttr {

Object attr;

public void test() {

attr = new Object();

}

}

再看下面这段代码,对象通过 return 语句返回。由于程序并不能确定这个对象后续会不会被使用,外部的线程能够访问到这个结果,对象也发生了逃逸。public class EscapeReturn {

Object attr;

public Object test() {

Object obj = new Object();

return obj;

}

}

那逃逸分析有什么好处呢?

1. 栈上分配

如果一个对象在子程序中被分配,指向该对象的指针永远不会逃逸,对象有可能会被优化为栈分配。栈分配可以快速地在栈帧上创建和销毁对象,不用再分配到堆空间,可以有效地减少 GC 的压力。

2. 分离对象或标量替换

但对象结构通常都比较复杂,如何将对象保存在栈上呢?

JIT 可以将对象打散,全部替换为一个个小的局部变量,这个打散的过程,就叫作标量替换(标量就是不能被进一步分割的变量,比如 int、long 等基本类型)。也就是说,标量替换后的对象,全部变成了局部变量,可以方便地进行栈上分配,而无须改动其他的代码。

从上面的描述我们可以看到,并不是所有的对象或者数组,都会在堆上分配。由于JIT的存在,如果发现某些对象没有逃逸出方法,那么就有可能被优化成栈分配。

3.同步消除

如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。

注意这是针对 synchronized 来说的,JUC 中的 Lock 并不能被消除。

要开启同步消除,需要加上 -XX:+EliminateLocks 参数。由于这个参数依赖逃逸分析,所以同时要打开 -XX:+DoEscapeAnalysis 选项。

比如下面这段代码,JIT 判断对象锁只能被一个线程访问,就可以去掉这个同步的影响。public class SyncEliminate {

public void test() {

synchronized (new Object()) {

}

}

}

小结

JIT 是现代 JVM 主要的优化点,能够显著地提升程序的执行效率。从解释执行到最高层次的 C2,一个数量级的性能提升也是有可能的。

注意:JIT 优化并不见得每次都有用,比如代码中如果发生死循环。但如果你在启动的时候,加上-Djava.compiler=NONE 参数,禁用 JIT,它就能够执行下去。

这篇文章中我们主要看了方法内联、逃逸分析等概念,了解到一些方法在被优化后,对象并不一定是在堆上分配的,它可能在被标量替换后,直接在栈上分配。这几个知识点也是在面试中经常被问到的。

JIT 的这些优化一般都是在后台进程默默地去做了,我们不需要关注太多。同时Code Cache 的容量达到上限,会影响程序执行的效率,但除非你有特别多的代码,默认的 240M 一般来说,足够用了。

jit java同步消除_聊聊JIT是如何影响JVM性能的相关推荐

  1. java 同步锁_死磕 java同步系列之自己动手写一个锁Lock

    问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...

  2. java 同步原语_你所不知道的有关Java 和Scala中的同步问题

    在实际应用中所有的服务端程序都需要在多线程之间进行某种同步.大多数同步已经有框架完成了,比如我们的web服务器,DB客户端和消息框架.Java和Scala提供了大量的组件用来实现稳定的多线程程序.包括 ...

  3. jit java 怎么配置_新的Java JIT编译器Graal简介

    在本教程中,我们将深入研究名为Graal的新Java实时(JIT)编译器. 让我们首先解释JIT编译器的作用. 当我们编译Java程序时(例如,使用  javac命令),我们最终将源代码编译成代码的二 ...

  4. java final 内存_聊聊 Java 内存模型

    原标题:聊聊 Java 内存模型 *作者:青芒@有赞 本文目录 Java内存模型 重排序 内存屏障 volatitle的内存语义 final的内存语义 一.Java内存模型 硬件处理 电脑硬件,我们知 ...

  5. java 获取泛型_聊聊Java泛型擦除那些事

    >版权申明]非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89789849 出自:shushen ...

  6. java解决异常_聊聊Java中的异常及处理

    在编程中异常报错是不可避免的.特别是在学习某个语言初期,看到异常报错就抓耳挠腮,常常开玩笑说编程1分钟,改bug1小时.今天就让我们来看看什么是异常和怎么合理的处理异常吧! 异常与error介绍 下面 ...

  7. mysql主从数据库同步问题_聊聊MySQL主从数据库同步的那些事儿

    在linux服务器下测试:主(master)服务器IP:219.223.5.105(对应内网IP:192.168.1.75)从(slave)服务器IP:219.223.5.104(对应内网IP:192 ...

  8. java的prefetch()_聊聊FluxFlatMap的concurrency及prefetch参数

    序 本文主要研究下FluxFlatMap的concurrency及prefetch参数 实例 @Test public void testConcurrencyAndPrefetch(){ int c ...

  9. java redis监控工具_聊聊redis的监控工具

    序 本文主要研究一下redis的监控工具 redis-stat redis-stat是一个比较有名的redis指标可视化的监控工具,采用ruby开发,基于redis的info命令来统计,不影响redi ...

最新文章

  1. mysql mha官网下载_mysql MHA 及多主复制
  2. ITAA在线试学用户使用说明
  3. OpenJudge 2739 计算对数
  4. boost::initialized<T>相关的测试程序
  5. springboot属性注入
  6. argo 现水下永动机器人_现水下永动机器人 水下永动机器人有什么作用?
  7. Dotnet中Span, Memory和ReadOnlySequence之浅见
  8. PHP 数据库 ODBC
  9. python常用的装饰器有哪些_python基本装饰器
  10. 设计模式---面向对象的设计原则概述
  11. CentOS图形界面和命令行切换
  12. Android GMS重要工具和资料下载
  13. android recovery 3e,【记录】尝试用ODIN去恢复启动界面是Android system recovery 3e的I9100G...
  14. 广州IT销售菜鸟总结精华
  15. C语言文件操作FILE文件指针fopen文件打开操作
  16. 关于触摸板设备USB映射虚拟桌面的VID与PID
  17. java的serialization_Java序列化(Serialization) 机制
  18. 【NYOJ】[845]无主之地1
  19. 阿里云 SDK 动态IP域名解析
  20. RVMedia组件Crack,IP 摄像机接收视频

热门文章

  1. 托福、雅思、GRE、SAT、GMAT
  2. 治五气,艺五种,抚万民,度四方
  3. 手机性能测试指标及操作
  4. Qt Creator嵌入式python时报错error: ::hypot has not been declared
  5. 机器学习可行性与VC dimension
  6. java中paint_Java中paint怎么用
  7. element-ui calendar 日历控件实现编辑假期功能以及样式修改
  8. win10注册表损坏开机蓝屏修复
  9. netty源码分析7-NioEventLoop-run方法疑难点
  10. Linux---挂载和卸载移动硬盘、开机自启动机械硬盘