JDK体系结构
JDK:JDK提供了编译、运行Java程序所需的各种资源和工具;包括Java编译器,Java运行时环境:JRE;开发工具包括编译工具(javac.exe)打包工具(jar.exe)等。
JRE: 即JAVA运行时环境,JVM就是包括在JRE中,以及常用的JAVA类库等;
SDK: SDK是基于JDK进行扩展的,是解决企业级开发的工具包。如JSP、JDBC、EJB等就是由SDK提供的 ;
Java语言的跨平台特性
在多平台上都有自己的JDK编译器,当运行时在各自的虚拟机中运行。
JVM整体结构及内存模型
public class Math {
public static User user = new User();
public Math() {
}
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();
}
}
new出来的对象一般放在堆里,有时放在栈上。栈内存区域(线程栈),只要有一个线程开始运行我们的主方法,Java虚拟机会在我们的线程栈分配独立的空间,用来存储运行过程中的局部变量。栈内部结构比较复杂:
栈帧:当main方法运行,会分配一个专属的内存栈,用来放main方法的局部变量。compute方法执行,又会分配一个栈帧空间,放compute方法内部的变量,一个方法对应独立的栈帧内存区域,栈帧把不同方法内存隔离,栈帧和程序的调用顺序一致,后分配的空间先释放掉。一个方法分配一个新的内存栈帧空间。栈帧内存空间除了存局部变量,还有操作数栈,动态链接,方法出口。(局部变量不是栈结构,操作数栈是)
程序计数器:每个线程独有,放程序运行代码的内存位置,也叫行号,指定了运行代码的内存位置。
Math.class最终是加载到方法区,字节码执行引擎执行方法区的Math,当其他线程抢占了,恢复时恢复到程序计数器的位置,将操作数栈的数据取出,执行iadd,结果重新压回操作数栈,继续执行,将10压入操作数栈,将操作数栈数据取出,执行imul,得到30,压回操作数栈,是操作数存储值的地方,是做计算的临时的中转内存空间。
动态链接:把符号引用转变为直接引用,在程序运行过程中,compute是常量池里的符号,运行到时解析这个符号,找这个符号对应的代码,将符号引用转换为符号对应的代码的在内存里的直接地址,时符号的直接引用代码。
方法出口,方法执行完后返回,要返回哪个位置放在方法出口中
main栈帧局部变量表,new的Math对象放在堆里,局部变量math在局部变量表中,存放math在堆中的内存地址。
方法区:常量池放到方法区,运行时常量池放到方法区。
组成:常量+静态变量+类信息,静态变量如果是对象,放在堆里,user变量指向它。
本地方法栈:native本地方法,C++实现,如果需要调用到本地方法,分配内存空间,线程栈里也有本地方法栈,堆和方法区市所有线程共享的区域,栈、本地方法栈、程序计数器是每一个线程独有的。
堆:包含老年代,年轻代,年轻代包含Eden区,Survivor区,默认年轻代占1/3,老年代占2/3,Eden区和Survivor区比例为8:1:1,当Eden区放满后触发minor gc(由字节码执行引擎执行)。
gc root根节点:包括线程栈的本地变量、静态变量、本地方法栈的变量等等。做垃圾回收时,非垃圾对象赋到S0(Survivor0),Eden区的其他对象即为垃圾对象直接干掉,一个对象经历过一次gc还存活,它的分代年龄会+1,分代年龄存在对象头;第二次出发minor gc不但回收Eden区,还回收S0区,S0以及Eden还存活的对象放到S1,分代年龄+1,下次gc再移到S0,经历了15次gc的对象放入老年代,如果老年代满了触发FullGC,回收堆和方法区,再放满则触发OOM。
STW:停止整个世界,Full GC STW的时间长Eden区STW的时间短,几乎察觉不到。为什么要STW?GC过程中找到的对象,但这个对象随程序执行结束变为垃圾对象,之前认为是非垃圾对象,至少在找对象的过程中STW。方法区(元空间)用的直接内存,当方法区满了(默认25M),会触发FullGC。初始大小会自动调小,所以-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M,方法区一定要设置。