JVM --执行引擎

两种执行引擎解释器

如何理解Java是半编译半解释型语言

  • javac编译,java运行
  • 字节码解释器解释执行,模板解释器编译执行
字节码解释器
  • 解释执行,每行java字节码先解析为c++,再解析为硬编码,一行一行执行
  • 效率偏低,小项目可以使用
CASE(_new): {
        u2 index = Bytes::get_Java_u2(pc+1);
        ConstantPool* constants = istate->method()->constants();
        if (!constants->tag_at(index).is_unresolved_klass()) {
          // Make sure klass is initialized and doesn't have a finalizer
          Klass* entry = constants->slot_at(index).get_klass();
          assert(entry->is_klass(), "Should be resolved klass");
          Klass* k_entry = (Klass*) entry;
          assert(k_entry->oop_is_instance(), "Should be InstanceKlass");
          InstanceKlass* ik = (InstanceKlass*) k_entry;
          if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
 ……
模板解释器
  • 通过即时编译先将字节码对应的代码编译生成硬编码,直接执行硬编码就可,效率高
  • 执行的硬编码就是即时编译器给编译的
void TemplateTable::_new() {
  transition(vtos, atos);

  Label slow_case;
  Label done;
  Label initialize_header;
  Label initialize_object;  // including clearing the fields

  Register RallocatedObject = Otos_i;
  Register RinstanceKlass = O1;
  Register Roffset = O3;
  Register Rscratch = O4;

  __ get_2_byte_integer_at_bcp(1, Rscratch, Roffset, InterpreterMacroAssembler::Unsigned);
  __ get_cpool_and_tags(Rscratch, G3_scratch);
  // make sure the class we're about to instantiate has been resolved
  // This is done before loading InstanceKlass to be consistent with the order
  // how Constant Pool is updated (see ConstantPool::klass_at_put)
  __ add(G3_scratch, Array<u1>::base_offset_in_bytes(), G3_scratch);
  __ ldub(G3_scratch, Roffset, G3_scratch);
  __ cmp(G3_scratch, JVM_CONSTANT_Class);
  __ br(Assembler::notEqual, false, Assembler::pn, slow_case);
……    

即时编译器

C1
  • c1编译器是client模式下的即时编译器
  • 需要收集的数据较少,即达到触发即时编译的条件较宽松
  • 自带的编译优化优化的点较少:基本运算在编译的时候运算掉了
  • 编译时较C2,没那么耗CPU,带来的结果是编译后生成的代码执行效率较C2低
C2
  • c2编译器是server模式下的即时编译器
  • 触发的条件比较严格,一般来说,程序运行了一段时间以后才会触发
  • 需要收集的数据较多
  • 编译时很耗CPU
  • 编译优化的点较多
  • 编译生成的代码执行效率较C1更高
混合编译
  • 程序运行初期因为产生的数据较少,这时候执行C1编译,程序执行一段时间后,收集到足够的数据,执行C2编译器
即时编译触发条件
  • 目前的64bit机器上只有server模式。执行引擎是server模式启动的JVM中的执行引擎
  • 触发即时编译的最小单位是代码段,最大单位是方法
  • Client 编译器模式下,N 默认的值 1500 – C1
  • Server 编译器模式下,N 默认的值则是 10000 – C2
热度衰减
  • 即时编译器会记录触发条件值N,
  • 如果一段时间N没有变化,就会按照2倍速度衰减
  • 就会导致后面在编译的时候N值降低,需要编译更多
热点代码缓存区
  • 保存在方法区的,也是调优需要调的地方 InitialCodeCacheSize/ReservedCodeCacheSize
  • server 编译器模式下代码缓存大小则起始于 2496KB
  • client 编译器模式下代码缓存大小起始于 160KB
  • 查看 : java -XX:+PrintFlagsFinal -version | grep InitialCodeCacheSize
集群扩容热点代码缓存带来的问题
  • 热机情况下缓存区缓存数据比较完善,处理代码效率相对较高,
  • 集群扩容切入冷机的时候,冷机没有热点缓存,不能直接按照集群中热机的流量进行切入
  • 热点缓存过程中逐步提升流量

逃逸分析

  • 默认开启
  • 逃逸:逃出当前作用域
基于逃逸分析的三种优化
  • 栈上分配
    ○ 不会逃逸出当前栈帧(方法),如成员变量,直接分配到虚拟机栈上
  • 标量替换
    ○ 标量:不可再分,java中的基本数据类型就是标量
    ○ 聚合量:可再分,对象
    ○ jvm逃逸分析发现使用的对象的某些变量没有逃逸情况,并且是常量,就会在编译的时候直接用常量替换这个对象.属性的获取值方式
  • 锁消除
public void test(){
    //new Object没有逃逸,jvm经过逃逸分析认为中国锁是无效的,编译的时候就会去掉这个锁,相当于无锁
    synchronized (new Object()){
        System.out.println("hello");
    }
}

三种运行模式

JIT为什么能提升性能呢?原因是运行期的热点代码编译与缓存

JVM中有两种即时编译器,就诞生了三种运行模式

  • -Xint:纯字节码解释器模式 效率最低
  • -Xcomp:纯模板解释器模式
  • -Xmixed:字节码解释器+模板解释器模式(默认)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值