虚拟机内联缓存

内联缓存:作用是进一步压缩查找过程,提高对象的查找效率,他就是一个代码中的监听点,在执行代码时,内联缓存会观察函数中的一些调用点,然后把这些调用点上使用的map存储到反馈向量中,每一个优化的函数都会维护一个自身的反馈向量,记录执行过程中需要的关键数据

内联缓存是一种加快动态分派的优化技术。它能够缓存虚方法调用中调用者的动态类型,以及该类型所对应的目标方法。在之后的执行过程中,如果碰到已缓存的类型,内联缓存便会直接调用该类型所对应的目标方法。如果没有碰到已缓存的类型,内联缓存则会退化至使用基于方法表的动态分派。

JVM 是如何捕获异常的?
提到 JVM 处理异常的机制,就不得不提 Exception Table,称为异常表.

异常表中包含了一个或多个异常处理者(Exception Handler)的信息,这些信息包含如下

  • from 可能发生异常的起始点
  • to 可能发生异常的结束点
  • target 上述from和to之前发生异常后的异常处理者的位置
  • type 异常处理者处理的异常的类信息

那么异常表用在什么时候呢?

答案是异常发生的时候,当一个异常发生时

JVM会在当前出现异常的方法中,查找异常表,是否有合适的处理者来处理。
如果当前方法异常表不为空,并且异常符合处理者的from和to节点,并且type也匹配,则JVM调用位于 target 指针指向的字节码来处理。
如果上一条未找到合理的处理者,则继续查找异常表中的剩余条目。
如果当前方法的异常表无法处理,则向上查找(弹栈处理)刚刚调用该方法的调用处,并重复上面的操作。
如果所有的栈帧被弹出,仍然没有处理,则抛给当前的Thread,Thread则会终止。
如果当前Thread为最后一个非守护线程,且未处理异常,则会导致JVM终止运行。
以上就是JVM处理异常的一些机制。

反射调用的实现
MethodAccessor 有两个版本的实现。
1:一个是Java实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;
2:另一个是 native code 实现的。native 版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过 Java 版了。这是 HotSpot 的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越 native 边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
让 Java 方法在被反射调用时,开头若干次使用 native 版,等反射调用次数超过阈值(15次)时则生成一个专用的 MethodAccessor 实现类,生成其中的 invoke() 方法的字节码,以后对该 Java 方法的反射调用就会使用 Java 版。

方法的反射调用会带来不少性能开销,原因主要有三个:变长参数方法导致的 Object 数组,基本类型的自动装箱、拆箱,还有最重要的方法内联。

方法内联是指,在编译过程中,当遇到方法调用时,将目标方法的方法体纳入编译范围之中,并取代原方法调用的优化手段。
Java 虚拟机中的即时编译器会使用内联缓存来加速动态绑定。
Java 虚拟机所采用的单态内联缓存将纪录调用者的动态类型,以及它所对应的目标方法。
当碰到新的调用者时,如果其动态类型与缓存中的类型匹配,则直接调用缓存的目标方法。
否则,Java 虚拟机将该内联缓存劣化为超多态内联缓存,在今后的执行过程中直接使用方法表进行动态绑定。

内联缓存是一种加快动态绑定的优化技术。它能够缓存虚方法调用中调用者的动态类型,以及该类型所对应的目标方法。

逃逸分析:
逃逸分析是“一种确定指针动态范围的静态分析,它可以分析在程序的哪些地方可以访问到指针”。Java虚拟机的即时编译器会对新建的对象进行逃逸分析,判断对象是否逃逸出线程或者方法。即时编译器判断对象是否逃逸的依据有两种:

对象是否被存入堆中(静态字段或者堆中对象的实例字段),一旦对象被存入堆中,其他线程便能获得该对象的引用,即时编译器就无法追踪所有使用该对象的代码位置。

简单来说就是,如类变量或实例变量,可能被其它线程访问到,这就叫做线程逃逸,存在线程安全问题。

对象是否被传入未知代码中,即时编译器会将未被内联的代码当成未知代码,因为它无法确认该方法调用会不会将调用者或所传入的参数存储至堆中,这种情况,可以直接认为方法调用的调用者以及参数是逃逸的。(未知代码指的是没有被内联的方法调用)

比如说,当一个对象在方法中定义之后,它可能被外部方法所引用,作为参数传递到其它方法中,这叫做方法逃逸,

没有逃逸的对象:
1栈上分配:方法执行完后自动销毁,而不需要垃圾回收的介入
2:同步消除:单线程中是没有锁竞争
3:标量替换:可以各自分别在栈帧或寄存器上分配空间,原本的对象就无需整体分配空间了

程序计数器:
线程内存独享,占用内存小,生命周期与线程相同,记录下一条指令所在的地址
虚拟机栈:
每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息在这里插入图片描述

本地方法栈
本地方法栈为本地方法服务。

所以,Java 堆可细分为新生代和老年代;再细分一点的话新生代可细分为 Eden 区、From Survivor 区、To Survivor 区。
java -Xms512M -Xmx512M HelloTest
方法区
用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。
对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。
HotSpot 虚拟机把它当成永久代来进行垃圾回收。但是很难确定永久代的大小,因为它受到很多因素影响,并且每次 Full GC 之后永久代的大小都会改变,所以经常会抛出 OutOfMemoryError 异常。为了更容易管理方法区,从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值