先看一段代码:
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()方法的过程:
-
首先在mian线程中会有栈、本地方法栈、程序计数器三个区域,在栈这个区域会有add方法栈帧,main方法栈帧,而且在每个栈帧里面都有局部变量表、操作数栈、方法出口,从反编译的class文件中,我们可以看见很多指令,这些指令都是程序的执行过程。
-
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方法的执行过程。
-
图示分析如下:
下面来一个面试题: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学习资料,可添加微信公众号领取