以下内容参考《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》
JVM:全称JAVA Virtual Machine。它是一种基于计算机设备的规范,是一种虚拟机。
通过JVM,JAVA实现了一次编译到处运行,只需要在需要运行的平台部署JVM即可。
JRE:全称Java Runtime Environment,JVM的运行平台,是Java的运行时环境,JRE = 虚拟机平台+虚拟机本体(JVM)
JDK:全称Java Develop Kit, Java开发工具包,本身也是一种Java程序,所以需要依赖JRE,为了保持JDK的独立性和完整性,所以通常安装目录下会附带JRE
JVM内存区域的结构
线程私有:虚拟机栈、方法栈、程序计数器
线程公有:堆、方法区
程序计数器
程序计数器是一块非常小的内存空间,因为是线程私有的,它也可以看作是 当前线程执行的字节码的行号指示器,字节码解释器工作的时候就是通过改变计数器的值来决定下一条需要执行的字节码指令,分支、跳转、循环、异常处理等都是通过计数器来实现的。
如果线程在执行的是Java方法,那么程序计数器记录的是虚拟机字节码指令的地址。
如果线程在执行的时候调用的是Native方法(指Java调用非Java接口,接口的实现为非Java语言实现),计数器为空。
虚拟机栈
生命周期和线程相同,虚拟机栈描述的是JVM内存模型。每次方法调用的时候都会创建一个栈帧栈帧中存储的是:局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法调用到结束的过程,就是一个栈帧入栈到出栈的过程。
局部变量表:局部变量表中存储的都是编译器可知的基本数据类型(int、long、float、boolean、double、short、byte、char)、对象引用类型(指向对象起始地址的引用指针)、returnAddress类型(指向字节码的地址)
本地方法栈
本地方法栈的作用和虚拟机栈作用是相似的,区别是虚拟机站执行的是java方法,本地方法栈执行的是Native方法
堆
堆是JVM虚拟机中管理的内存最大的一块,这个区域的作用就是存放对象的实例。几乎所有的对象实例都在这里分配内存,这块区域也是垃圾收集器(GC)管理的主要区域。从内存回收的角度来看,现在回收的算法一般都是分代收集算法,所以堆也可以细分为新生代和老年代。从内存分配的角度来看,线程共享的Java堆会在某些情况下(例如:多线程)分配线程私有的分配缓冲区。不管怎么分配,Java堆都是存放对象的实例,划分这些模块只是为了更好、更快的回收内存。
根据Java虚拟机规范的规定,Java堆可以处以物理上不连续的内存空间,只要逻辑上连续就行。在实现上可以是固定大小的,也可以是可扩展的,当前虚拟机主流都是可扩展的
方法区
方法区和堆一样都是线程共享的区域,方法区存储的是类信息、常量、静态变量、即使编译器编译后的代码等数据。
很多人把方法区称为永久代,因为虚拟机对方法区的规范很宽松,除了和Java堆一样不需要连续的内存空间和可以选择的固定大小或者可扩展之外,最重要的一点是可以选择不实现GC,垃圾收集行为在这个区域是非常少见的,但是并不是因为进入方法区之后就“永久”的存在了,这块区域的主要回收目标是常量池的回收和对类型的卸载。
运行时常量池:Class文件除了类的版本、字段、方法、接口等描述信息外,还有一个就是常量池,用于存放编译期生成的各种字面量和符号引用,这部分会在类加载后进入方法区的运行时常量池存放。
运行时常量池相较于Class常量池另外一个重要的特征就是具备动态性,Java并不要求常量要在一开始即编译期就存在,运行期间的常量也可以加入到常量池中。