java虚拟机问题2

11.java中的synchronezed实现

  1. 重量级锁:java线程的阻塞和唤醒都是依靠操作系统来完成的,需要操作系统从用户态切换至内核态,开销相当大。为避免这种情况,Java 虚拟机会在线程进入阻塞状态之前,以及被唤醒后竞争不到锁的情况下,进入自旋状态。通过动态设置自旋等待时间,来减少锁的开销。如果上次自旋获取到了锁,那么这次就延长自旋时间,如果上次自旋没有获取到锁,这次就缩短自旋时间。

  2. 轻量级锁:不同线程获取锁的时候,没有出现过线程等待的情况,比如说获取锁的时间刚好都岔开,那么就是轻量级锁。锁的获取是通过CAS原子操作实现的。

  3. 偏向锁:只有一个线程会访问当前锁。如果其余线程访问当前锁的次数超过默认值20,则会将偏向锁修改为轻量级锁。开销会很大。

12.java中的泛型是怎么实现的

  1. java的泛型在进入jvm会被擦除掉。泛型的类型为最高父类。如果泛型为T则会转为Object,如果泛型为T extends Father则会转为Father类。

  2. java会传出的参数,返回值 的泛型 进行强转。只不过这个过程由编译器来做,不用开发人员写了。

  3. java的的类型校验也是由编译器来做,虚拟机不管。

13.即时编译

  1. 从java8开始默认采用分层编译的方式。将执行分为五个层次,0层的解释执行,1层执行没有 profiling的C1代码,2层执行部分 profiling的C1代码,3层执行全部profiling的C1代码,和4层执行C2 代码。

  2. 通常情况下,方法首先会被解释执行,然后会被三层的c1编译,最后会被4层的c2编译。

  3. 即时编译的优化,还有一种叫分支优化。将一直被执行的代码直接编译为机器码,未执行过的代码打一个错误标记。如果不小心执行到错误标记处,则会从即时编译退回至解释执行。

14.方法内联

  1. 方法内联指的是,在编译过程中,遇到方法调用,将目标方法的方法体纳入编译范围之内。

  2. 静态方法的调用,和final类型的方法,可以直接确定为唯一调用目标。

  3. 动态调用的方法invokeViture和invokeInterface会进行动态绑定。
    动态绑定的内联分为两种:
    1.完全去虚化,通过类型推导或者类层次分析识别虚方法调用的唯一目标方法,关键是证明虚方法调用是唯一的。
    2.条件去虚化:是将虚方法调用转化为若干个类型测试以及直接调用的一种优化手段。关键在于找出需要比较的类型。条件去虚化是通过java的Profile或者方法表实现的。

15.逃逸分析

  1. 判断对象是否逃逸的依据,一是对象是否被存入堆中,比如说静态字段或者堆中对象的引用,二是是否被传入未知代码中。

  2. 即时编译器可以根据逃逸分析做出一些优化,比如说栈上分配,锁消除,标量替换等。主要还是根据对象是否会被外部使用,来优化一些相关的不必要操作。

锁消除:synchronized (new Object()) {}的锁会被完全优化。

标量替换:List集合被外部使用,如果遍历的话,记录的是cursor作为当前指针指向,expectedModCount作为集合最大位置。

栈上分配:由于java对象都是存在堆中的,所以没有使用栈上分配这个优化手段。

  1. 部分逃逸分析。部分逃逸分析和部分代码内联相似。通过对代码执行热度的分析,来对一些代码进行相应的逃逸分析优化。一般出现在if else中,例如其中一种可能出现概率为1%另一种出现概率为99%,则会出现1%的情况的时候,则会使用逃逸分析优化。

16.字段访问相关优化

  1. 在逃逸分析中,如果对象本身是逃逸的,或者因为方法内联不够彻底,对象被当作是逃逸的,就无法让即时编译器进行标量替换。这时候需要字段访问优化。

示例:

public class mytest {

    public static Foo foo = new Foo();

    public static void main(String[] args) {
        bar(foo,1);
    }

    public static int bar(Foo foo,int x){
        foo.a = x;
        return foo.a;
    }

}

class Foo{
    public int a;
}

在逃逸分析中,bar方法的foo会被当作是逃逸的,所以在无法进行标量替换,这段代码会进行两次内存操作,存储和读取 foo.a 我们其实可以轻易的将其优化为 return x;
但是foo对象是逃逸的,所以无法保证这样一定是安全的,因此会在foo.a加上内存屏障,如果foo.a被执行写操作,刷新缓存,获取foo.a的值,如果foo.a没有执行写操作,则使用缓存中x的值。

  1. 无效代码优化和死代码优化。

无效代码优化示例:

class Foo {
  int a = 0;
  void bar() {
    a = 1;
    a = 2;
  }
}

其中a=1这步,会被优化掉,因为是无效代码。

死代码优化示例:

int bar(int x) {
  if (false)
    return x;
  else
    return -x;
}

会被直接优化为 return -x;因为上半部分为不可达代码。

17.循环优化

  1. 循环无关代码外提:

int foo(int x, int y, int[] a) {
  int sum = 0;
  for (int i = 0; i < a.length; i++) {
    sum += x * y + a[i];
  }
  return sum;
}

会将循环中x*y的值,提取到循环外计算好,并存储起来。
2. > 循环展开,是否进行循环展开,主要是看循环次数是否超过一个固定的阈值。

展开前:

int foo(int[] a) {
  int sum = 0;
  for (int i = 0; i < 4; i++) {
    sum += a[i];
  }
  return sum;
}

展开后:

int foo(int[] a) {
  int sum = 0;
  sum += a[0];
  sum += a[1];
  sum += a[2];
  sum += a[3];
  return sum;
}

18.java虚拟机的监控以及诊断工具

  1. Eclipse MAT 和 JMC

  2. Eclipse MAT 可用于分析jmap命令导出的java堆快照。主要包括两个比较重要的视图,直方图和支配树。直方图展示了各个类的实例书目,以及这些实例的Shallow heap(对象自身所占用的内存)和Retained heap(垃圾回收所能回收的总内存)。支配树则展示了快照种每个对象所直接支配的对象。

  3. JMC 是java平台的性能监控工具,主要是可以用JFR以极低性能开销手机java虚拟机的性能数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值