一、模型图
二、JVM内存分区
JVM运行时数据区的内存有:方法区、Java堆、Java栈、本地方法栈、程序计数器
方法区:方法区是被所有线程共享的内存区域,用来存储已被虚拟机加载的类信息、常量、静态变量、JTI(just in time,即时编译技术)编译后的代码等数据。运行时常量池是方法区的一部分,用于存放编译期间生成的各种字面常量和符号引用。平时通过反射获取到的类型、方法名、字段名称、访问修饰符等信息就是从方法区获取到的。在使用到CGLib对类进行增强时,增强的类越多,就需要越大的方法区类存储动态生成的Class信息,当存放方法区数据的内存溢出时,会报OutOfMemoryError异常,在jdk1.8中也就是Metaspace内存溢出,可以通过参数JVM参数-XX:MetaspaceSize和-XX:MaxMetaspaceSize设置Metaspace的空间大小。
-
Java堆(Heap):和方法区一样是被所有线程共享的内存区域,是JVM中最大的一块内存区域,几乎所有的对象实例都是在这个区域进行内存的分配的,为对象分配内存主要有两种方式:
- 指针碰撞法:把堆中的内存进行划分(已分配的内存+空闲的内存),通过指针作为分界点,当需要分内存时,把指向空闲内存移动与对象大小相等的距离。
- 空闲列表法:JVM通过维护一个列表,记录可用的内存块信息,当分配操作发生时,从列表中找到一个足够大的内存分配给对象实例,并更新列表上的记录。
-
Java栈(Java Stack):又称之为虚拟机栈,存放的是栈帧(栈的基础单位), 是用来存储数据和部分过程结果的数据结构。
其中:
1.局部变量表:用于存储方法参数和方法内部定义的局部变量,对于非static方法,局部变量表存储的第0个位置存储的为当前对象的引用,可以通过关键字this进行访问,方法参数按照参数列表顺序,从第1个位置分配下去;
2.操作数栈:用于操作运行过程中的各种中间结果与字节码指令;
3.动态链接:程序运行过程中,将符号引用解析为直接引用,例如多态,对象的引用在程序运行时链接到具体的实现类;
4.出口记录:程序的出口 本地方法栈(Native Method Stack)
本地方法栈是与Java栈发挥的作用十分相似,区别是Java栈执行的是Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的本地方法服务,可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是本地方法所调用的c代码。程序计数器(Program Counter Register):用来记录当前方法执行的情况,为了在线程切换可以恢复到正确执行位置,每个线程都需有独立的一个程序计数器,不同线程之间的程序计数器互不影响,独立存储。
注意:如果线程执行的是个java方法,那么计数器记录虚拟机字节码指令的地址。如果为native【底层方法】,那么计数器为空。这块内存区域是虚拟机规范中唯一没有OutOfMemoryError的区域。
注:Java栈、本地方法栈、程序计数器生命周期一样,都是线程私有,随线程的创建而创建