死磕Java虚拟机,面试百分百必问

先看一段代码:

public class HelloWorld {

    // 类的属性 :常量、变量、成员属性
    private Object object = new Object();
    private static int i = 0;
    private static String s = "Hello World";

    /**
     * 计算方法 局部变量
     * @return
     */
    public int add(){
        int a = 1;
        int b = 2;
        int c = (a + b) * 100;
        return c;
    }

    /**
     * 程序入口
     * @param args
     */
    public static void main(String[] args) {
        HelloWorld app = new HelloWorld();
        int result = app.add();
        System.out.println(result);
    }

}

 

HelloWorld.class文件进入虚拟机,到执行main方法是怎么进行的,我们来一步步讲解

 

JVM主要分为三个区域:类加载子系统、JVM运行时数据区、执行引擎,当然我们面试被问到最多的就是JVM运行时数据区

关于class文件是如何进入JVM运行时数据区的,执行引擎是如何执行的?

 

先使用javap -c HelloWorld.class > App.txt ,把HelloWorld.class反汇编并导出到txt文本。文本如下:

 

Compiled from "HelloWorld.java"
public class com.fengyaof.jvm.sikejvm.HelloWorld {
  public com.fengyaof.jvm.sikejvm.HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #2                  // class java/lang/Object
       8: dup
       9: invokespecial #1                  // Method java/lang/Object."<init>":()V
      12: putfield      #3                  // Field object:Ljava/lang/Object;
      15: return

  public int add();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        100
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #4                  // class com/fengyaof/jvm/sikejvm/HelloWorld
       3: dup
       4: invokespecial #5                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #6                  // Method add:()I
      12: istore_2
      13: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      16: iload_2
      17: invokevirtual #8                  // Method java/io/PrintStream.println:(I)V
      20: return

  static {};
    Code:
       0: iconst_0
       1: putstatic     #9                  // Field i:I
       4: ldc           #10                 // String Hello World
       6: putstatic     #11                 // Field s:Ljava/lang/String;
       9: return
}

 

下面我来分析一下add()方法的过程:

 

  1. 首先在mian线程中会有栈、本地方法栈、程序计数器三个区域,在栈这个区域会有add方法栈帧,main方法栈帧,而且在每个栈帧里面都有局部变量表、操作数栈、方法出口,从反编译的class文件中,我们可以看见很多指令,这些指令都是程序的执行过程。

     

  2. public int add();
    Code:
       0: iconst_1 将常量1压入操作数栈中
       1: istore_1 将数值1从操作数栈存储到局部变量表中
       2: iconst_2
       3: istore_2
       4: iload_1 将局部变量表中的1加载到操作数栈
       5: iload_2
       6: iadd 执行加法
       7: bipush   取值100     100
       9: imul  乘法运算
      10: istore_3
      11: iload_3
      12: ireturn 返回值
    

    从指令分析,就是add方法的执行过程。

     

  3. 图示分析如下:

     

下面来一个面试题:java为什么需要性能调优?

 

1. 性能调优主要调节堆内存,下面看下堆内存的划分:

1) 新生代的内存主要占堆内存的三分之一,老年代占三分之二,Eden和from、to占比是8:1:1

 

2) 98%的对象都是在我们的Eden区域创建(圣经中讲的是人类是在Eden元区创建),有2%的对象逃逸,大对象优先进入老年代。

 

3) Eden元区不断的有对象进入,直到对象占满,这时候会触发minor GC,minor GC会把游离态的对象清空,如果对象还在用着,则把对象放入from区域,此时此刻,Eden区域就是空的。触发一次minor GC的from区域就会age=1,

 

4) Eden元区又有不断的对象进入,当满了之后,就会触发第二次minor GC,然后from区域的对象age=2。

 

5) 当我们不断触发minor GC时,直到from区域占满,这时from区域和to区域就会交换角色,把from区域的对象复制放入to区域,如此反复的交换15次之后,如果发现对象还没有被回收,这时这个对象就会进入老年代。(15次的回收次数可以改)

 

6) 当我们的老年代满了的时候,这时候就会触发full GC,触发full GC的时候会出现STW(stop  the world ),不能提供任何的服务。

 

2. 为什么java采用分带回收策略?

  

  让对象更少的进入老年代,减少full GC的次数,其实就是金字塔的衰减过程。

 

想要更多的面试书籍和Java学习资料,可添加微信公众号领取

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值