-
JVM体系结构概览
-
类加载器(classLoader)
负责将.class文件加载到内存中,只负责加载,不负责运行,是否运行由execution engine决定。
类加载器主要分一下几种:
这里的Demo为自定义的类,所以类加载器为AppClassLoader,而Object为rt.jar包下面的原生类,所以类加载器为启动类加载器,而启动类加载器是C++写的,所以至为null
类加载器双亲委派机制:
当一个类加载器收到类加载请求,他首先不会自己去加载这个类,而是交给父类加载器去完成,因此所有的加载请求都应该传到启动类加载,只有当父类无法完成这个请求,子类才会尝试自己去加载
作用:
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
实际的String类中是没有main方法的所以报错了,如果导入进来会报下面的错
3. 本地方法栈(native method stack)
作用是融合不同语言为java所用,主要指调用C++接口,本地接口的方法由native修饰,在执行引擎(excution engine)执行的时候加载本地方法库(native libraries)。
如线程调用的时候其实就是调用本地接口private native void start0();
4. pc寄存器
即程序计数器(program counter register),就是一个指针,指向方法区中的方法字节码,一个方法执行完了,能够指向下一个需要执行的方法,相当于以前学校的值日表。
PC寄存器的作用
PC寄存器是用来存储指向下一条指令的地址,也即将将要执行的指令代码。由执行引擎读取下一条指令。
(1)它是一块很小的内存空间,几乎可以忽略不计。也是运行速度最快的存储区域
(2)在jvm规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致
(3)任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的java方法的JVM指令地址;或者,如果实在执行native方法,则是未指定值(undefined),因为程序计数器不负责本地方法栈。
(4)它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成
(5)字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
(6)它是唯一一个在java虚拟机规范中没有规定任何OOM(Out Of Memery)情况的区域,而且没有垃圾回收
-
方法区
供各线程共享的运行时内存区域。主要存储静态变量,常量,类信息(构造方法,接口定义),运行时的常量池。注意:实例变量存在堆内存中,与方法区无关。 -
栈
主管程序的运行,生命周期与线程同步,线程结束栈内存也就释放,所以不存在垃圾回收机制。
主要存储下面三种数据:
(1)本地变量,输入参数和输出参数以及方法内的变量
(2)栈操作,记录出栈入栈的操作
(3)栈帧数据:包括类文件方法等
栈:后进先出(LIFO-last in first out):最后插入的元素最先出来。
队列:先进先出(FIFO-first in first out):最先插入的元素最先出来。
主要存储下面三种数据:
(1)本地变量,输入参数和输出参数以及方法内的变量
(2)栈操作,记录出栈入栈的操作
(3)栈帧数据:包括类文件方法等
栈运行原理:
栈中的数据都是以栈帧(stack frame)的格式存在的,栈帧是内存区块。当一个方法a被调用时,就产生了一个栈帧F1被压入到栈中,a方法又调用了b方法,于是产生栈帧f2。。。每个方法执行的同时都会创建一个栈帧,用于存储局部变量表,操作数栈,每一个方法从调用到执行完毕就对应着一个栈帧在虚拟机中入栈到出栈的过程。栈的大小一般和具体JVM实现有关,通常为1Mb左右。
例:这里的a方法调用b方法,a先被调用入栈,b后入栈,所以结果中b方法的值先出,a方法的值后出
这里的方法m1不停入栈死循环,就会报StackOverflowError,这个不是一个Exception而是一个Error,也就是虚拟机错误
-
堆