jdk1.6:程序计数器、Java栈、本地方法栈、堆、方法区(包括:运行时常量)
jdk1.7:程序计数器、Java栈、本地方法栈、堆(包括:运行时常量)、方法区
jdk1.8:程序计数器、Java栈、本地方法栈、堆(包括:运行时常量)、元空间(本地内存)
程序计数器:
较小的内存空间,当前线程执行的字节码的行号指示器,各线程之间独立存储互不影响
Java栈:
线程私有,生命周期和线程相同,每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程,栈里面存放着各种基本数据类型和对象的引用
Java栈——局部变量表:
用于存放方法参数和方法内部定义的局部变量,在Java程序被编译成Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的最大局部变量表的容量
Java栈——操作数栈:
Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指操作数栈,和局部变量表一样,操作数栈也是被组织成一个以字长为单位的数组,但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作压栈和出栈来访问的,比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用
本地方法栈:
本地方法栈保存的是native方法的信息,当一个JVM创建的线程调用native方法后,JVM不再为其在虚拟机栈中创建栈帧,JVM只是简单地动态链接并直接调用native方法
堆:
所有线程共享的内存区域,存放对象实例,几乎所有的对象实例都在这里分配内存(但随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、变量替换优化技术会导致一些变化发生)
方法区:
和Java堆一样是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
运行时常量池:
运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量 ("zdy","123"等)和符号引用
字面量:文本字符串、声明为final的常量值等
符号引用:类和接口的完全限定名(包名+类名)、字段的名称和描述符、方法的名称和描述符
直接内存:
不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域
如果使用了NIO,这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作
这块内存不受java堆大小限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样)
类加载:
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、校验、准备、解析、初始化、使用和卸载7个阶段
加载:
通过类的全限定名称(包名+类名)来取得类的二进制字节流
把二进制字节流中的静态存储结构转化为方法区的运行时数据结构
在内存中生成这个类的java.lang.Class对象,作为方法区的这个类的各种数据的访问入口
校验:确保类的格式正确
文件格式验证
元数据验证
字节码验证
符号引用验证
准备:为类中的变量分配内存空间,并初始化默认值
解析:符号引用转换为直接引用
初始化:执行静态代码块、静态属性
什么时候执行类的初始化:
jvm启动时被标记为启动类的类(例如main方法)
调用类中的静态方法
访问类中的静态变量,或者对静态变量赋值
new操作创建对象的时候
通过反射加载类的时候
当初始化一个子类的时候,会先执行父类的初始化
类加载器:
类加载器是用来把类加载到jvm中
类的加载过程采用双亲委派模型
类加载器负责类加载过程中的加载阶段
Bootstrap ClassLoader(rt.jar) ——> Extension ClassLoader(lib/ext/*.jar) ——> Application ClassLoader(classpath) ——> Custom ClassLoader(自定义路径)
-Xbootclasspath/a:XXX路径 追加指定XXX目录到启动类加载器