Table of Contents generated with DocToc
笔记来源:图灵学院
一、java虚拟机概述
- java虚拟机用于将字节码文件.class转化为不同操作系统上的机器码
-
java虚拟机的内存模型
二、栈内存解析
2.1 概述
java虚拟机为每一个java线程分配一个栈内存空间,存放变量
public class Math{
public static final int initData = 666;//常量存储在方法区
public int compute() { //一个方法对应一块栈帧内存区域
int a=1;
int b=2;
int c = (a+b)*10;
return c;
}
public static void main(String[] args){
Math math = new Math();
math.compute();
}
}
a,b,c 保存在main线程的compute()栈帧中
2.2 栈帧内部结构
学习栈帧内部结构前,我们首先要阅读字节码文件。
javac Math.java
javap -c Math.class > Math.txt
打开生成的Math.txt,可以看到反汇编后的代码如下:
Compiled from "Math.java"
public class jvm.Math {
public static final int initData;
public jvm.Math();
Code:
0: aload_0
1: invokespecial #12 // Method java/lang/Object."<init>":()V
4: return
public int compute();
Code:
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: bipush 10
9: imul
10: istore_3
11: iload_3
12: ireturn
public static void main(java.lang.String[]);
Code:
0: new #1 // class jvm/Math
3: dup
4: invokespecial #25 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #26 // Method compute:()I
12: pop
13: return
}
2.2.1 我们来解析一下compute()里面的代码:
public int compute() { //一个方法对应一块栈帧内存区域
int a=1;
int b=2;
int c = (a+b)*10;
return c;
}
//=======================================
public int compute();
Code:
0: iconst_1 //将常量1压入操作数栈
1: istore_1 //将int类型的值存入局部变量1 -- a
2: iconst_2 //将常量2压入操作数栈
3: istore_2 //将int类型的值存入局部变量2 -- b
4: iload_1 //从局部变量1装载int类型的值
5: iload_2 //从局部变量2装载int类型的值
6: iadd //1+2
7: bipush 10
9: imul //*10
10: istore_3 //将int类型的值存入局部变量3 -- c
11: iload_3 //装载c的值
12: ireturn
赋值过程:
装载,执行过程(操作数栈:临时存储需要操作的值):
2.2.2 main函数内存区域
main中存储了一个math对象的指针,指向堆(堆中有为这个对象开辟的内存空间):
三、本地方法栈
早期 JAVA 需要与C/C++进行交互,所以用本地方法栈存储C/C++方法
四、堆
首先我们看看堆的结构:
堆假设只有600M的话,那么年轻代占了1/3,也就是200M,老年代占了2/3。最开始的时候,对象都存放在Eden(伊甸区)中。
当Eden区装满时,则字节码执行引擎开启一个线程,运行minor gc,扫描伊甸区中的对象,如果对象被引用了,则在s0区中复制一份,否则留在Eden中,被垃圾回收机制清理。
其他区域也是类似的处理(在Eden,s0,s1中移动时,“年龄“会加大,当达到15时,会转入老年区,controller、service、bean、数据库持久层、缓存都是常见的老年区对象):
当老年代区放满后,字节码执行引擎开启一个线程,运行full gc,对所有区域进行垃圾回收,减少堆的空间,但是老年区被引用的对象还是不会被回收,所以当老年区再次被放满溢出时,会触发OutOfMemoryError的异常,即java堆不够用了。
扩展:STW【stop the world】,当 JVM 调用gc清理堆时,会暂停掉所有用户程序,比如电商购物时,点击按钮后通常会卡顿一下
垃圾回收,减少堆的空间,但是老年区被引用的对象还是不会被回收,所以当老年区再次被放满溢出时,会触发OutOfMemoryError的异常,即java堆不够用了。
扩展:STW【stop the world】,当 JVM 调用gc清理堆时,会暂停掉所有用户程序,比如电商购物时,点击按钮后通常会卡顿一下
所以 JVM 调优就是:尽量想方设法减少 STW 时间