JVM由类加载子系统、运行时数据区、执行引擎组成。
类加载子系统
当启动java main时,c++首先会初始化一个jvm,然后构件类的加载系统,加载main方法所在类,最后调用main方法。
class文件通过加载,被分解为常量,静态方法,类信息,存储在运行时数据区的方法区内,被所有线程作为共享数据使用。
运行时数据区
运行时数据区分为两大部分,一部分是所有线程共享数据,堆和方法区(1.8之前的永久代)。
另一部分是各线程私有的数据区,虚拟机栈,本地方法栈,程序计数器。由于该部分是线程私有,所以没有并发带来的数据竞争问题。
堆
堆是运行时数据区最大的区域,存放了绝大多数对象,也包括了其他区的对象,其他区可能只持有了引用。所以堆也是并发要处理的最主要区域。
java创建对象自动分配内存,销毁对象回收内存,都是通过堆来实现的。
当堆的内存占用超过最大时,会产生OOM异常
方法区
方法区是类加载系统用于存放所加载的class文件信息,java程序获取方法区内的类信息就可以创建类实例。方法区是所有线程共享的,但是他的数据从创建后就不会被修改,所以也是线程安全的。
线程栈(虚拟机栈)
栈是每个线程独有的,由于线程的数量可能会很大, 所以单个线程的栈只能分配到很小,默认为1M。只能存放基本类型和对象引用。
当两个线程栈内都存放了同一个对象的引用时,就会产生并发问题。
栈的数据结构,就是先入后出的栈。使用栈的数据结构,是由于线程会执行方法,方法又会嵌套执行其他方法, 最早执行的方法最后才能结束,非常符合栈的特点。
栈内存放的是栈帧,每当开启一个方法时,都会产生一个栈帧。
本地方法区
和虚拟机栈类似,是给本地方法c++使用的。
程序计数器
多个线程共享一个cpu资源时,线程是交替执行的,当线程失去资源,处于等待时,需要记录它的位置,用于下次继续执行。程序计数器记录了每个线程的运行位置,程序计数器是由执行引擎修改。
指令前的数字就是位置
执行引擎
执行字节码指令,每次执行都会将执行位置记录到程序计数器