同样的java程序在不同平台生成的机器码是不同的。
运行代码时,把class文件加载到运行时数据区
只要有线程运行了,JVM就会给他分配一个JVM线程栈空间。当线程中的某个方法执行了,就给这个方法在jvm线程栈中分配一个内存空间(叫做栈帧),用于存放这个方法的局部变量,操作数,方法出口。
java代码
public class Add {
private static User user=new User();
user存在方法区中,指向队中的对象User
private final int x=9;
private static final int y=8;
public int add(){
int a=1;
int b=2;
int c=(a+b)*10;
return c;
}
public static void main(String[] args) {
Add a=new Add();
a存在线程栈的局部变量表中,并执行队中的Add对象。
int res=a.add();
}
}
JVM指令码:
指令码中的每条指令前面的序号存在程序计数器中。 局部变量表中存方法中声明的局部变量。 操作数栈中存计算时的操作数,中间数、结果等。add方法里的1,2,10这些都是操作数 动态链接: 方法出口:方法返回后,要执行哪一个指令。
程序计数器的值是谁修改的? 字节码执行引擎。jvm指令就是字节码执行引擎来执行的。
本地方法栈:
线程的start方法:private native void start0();
有native修饰的方法
最终是由c实现的,而执行c也需要一块内存
Compiled from "Add.java"
public class com.wj.lock.Add {
public com.wj.lock.Add();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 9
7: putfield #2 // Field x:I
10: return
public int add();
Code:
0: iconst_1 在栈帧中创建一个局部变量1,存在栈帧的局部变量表中。
1: istore_1 将int类型的值赋值给局部变量1。从操作数栈中弹出这个值赋值给局部变量1.
2: iconst_2 在栈帧中创建一个局部变量2,存在栈帧的局部变量表中。
3: istore_2 将int类型的值赋值给局部变量2。
4: iload_1 拿到局部变量1d的值
5: iload_2 拿到局部变量2d的值
6: iadd 执行int类型的加法,操作数为前两步拿到的。
7: iconst_10
8: imul 执行int类型的乘法,结果存入操作数栈
9: istore_3 结果从操作数栈中弹出,赋值给局部变量3。
10: iload_3 拿到局部变量3的值
11: ireturn 把上一步拿到的值返回
public static void main(java.lang.String[]);
Code:
0: new #2 // class com/wj/lock/Add 局部变量表中存了一个对象的引用。
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method add:()I
12: istore_2
13: return
static {};
Code:
0: new #5 // class com/wj/lock/User
3: dup
4: invokespecial #6 // Method com/wj/lock/User."<init>":()V
7: putstatic #7 // Field user:Lcom/wj/lock/User;
10: return
}
方法区(元空间)
存静态变量、常量、类信息
堆
垃圾回收是由字节码执行殷勤进行的
垃圾回收
GC Time:每个凸起都是依次垃圾回收,这时候,开始minor gc,清理新生代,eden区被清理,s0,s1交替。老年代增大。
当老年代满了,会进行full gc,回收堆中的所有区域。如果还是满,那就会内存溢出OOM.
垃圾收集器
垃圾分析
可达性分析
GC Roots为起点向下搜索。 哪些可以作为GC Roots:根是只会引用其他对象但不会被其他对象引用的。有线程栈的本地变量、方法区静态变量,本地方法栈的变量.
分析时首先把所有GC Root都找出来。
比如main方法中定义了一个对象user,这就是一个根节点,从它开始,还要找user对象的成员。
eden区增长一段时间后就没了,有些被回收了,有些进入了survivor区,非垃圾对象在s0和s1之间来回放。当对象分代年龄达到15时,进入老年代。老年代一直增长.
STW
为什么垃圾收集过程要STW,如果没有STW,那么GC会一直找下去,线程都结束了,GC还在进行。 而线程结束不意味着对象没有了,对象还在堆中,只是线程的局部变量没有了,GC roots根也就没有。没有了根,上下的对象就都成了垃圾。
jvm参数设置
假设程序运行时,每s产生60MB的对象, 14s占满edan,那么每14s就要minor gc。gc时要触发stw。但是可能有些线程执行的慢了,到了14s还没有执行结束,那这些线程用到的一些对象就不是垃圾,不会被回收,这部分对象就从edan一道survivor区。
这个时候会触发一个机制:如果要移到survivor区的对象很大,可以直接移到老年代。多大的对象需要直接移动到老年代?可以手动设置。
如果大小设置的不合理,会让老年代很快满了,导致频繁的full gc。