一.概述
网络上有一些有关JVM运行机制的文章和视频讲解的已经很详细了。本文主要是对零散的知识加上自己的理解进行梳理一下。
二.JVM运行机制
1.JVM启动流程
图
2.JVM内部结构
当JVM使用类装载器装载某个类时,它首先要定位对应的class文件,然后读入这个class文件,最后,JVM提取该文件的内容信息,并将这些信息存储到方法区,最后返回一个class实例。
图
(一).方法区
(1).什么是方法区?
方法区是系统分配的一个内存逻辑区域,是用来存储类型信息的。
(2).方法区的主要特点?
A.方法区是线程安全的。由于所有的线程都共享方法区,所以,方法区里的数据访问必须被设计成线程安全的。例如,假如同时有两个线程都企图访问方法区中的同一个类,而这个类还没有被装入JVM,那么只允许一个线程去装载它,而其它线程必须等待。
B.方法区的大小不必是固定的,JVM可根据应用需要动态调整。同时,方法区也不一定是连续的,方法区可以在一个堆(甚至是JVM自己的堆)中自由分配。
C.方法区也可被垃圾收集,当某个类不在被使用(不可触及)时,JVM将卸载这个类,进行垃圾收集。
(3).方法区里存的都是类型信息,也就是类的信息,而类的信息又包括以下内容:
A.类的全限定名、类的全路径名、类的直接超类的全限定名(如果这个类是Object,则它没有超类)。
B.这个类是类型类还是接口类。
C.访问修饰符,如public、abstract、final等。
D.所有的直接接口全限定名的有序列表(假如它实现了多个接口) 。
E.常量池、字段、方法信息、类变量信息(静态变量) 。
F.装载该类的装载器的引用(classLoader)、类型引用(class) 。
G.不过JDK6的时候,String等常量信息存在方法区中,JDK7的时候就移动到了堆中。
(二).虚拟机栈
(1).什么是java虚拟机栈?
描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈道出栈的过程。
(2).java虚拟机栈的主要特点?
A.线程私有。
B.后进先出(LIFO)栈。
C.存储栈帧,支撑java方法的调用、执行和退出。
D.可能出现OutOfMemoryError异常和StackOverflowError异常。
(三)本地方法栈
(1).什么是本地方法栈?
本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是java虚拟机栈是为了执行java方法(也就是字节码)服务的,本地方法栈是java虚拟机为了Native方法所服务的。在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。
(2).本地方法栈的主要特点?
A.线程私有。
B.后进先出(LIFO)栈。
C.作用是支撑Native方法的调用、执行和退出。
D.可能出现OutOfMemoryError异常和StackOverflowError异常。
(3).栈帧的概念和特征
A.Java虚拟机栈中存储的内容,它被用于储存数据和部分过程结果的数据结构,同时也被用来处理动态链接、方法返回值和异常分派。
B.一个完整的栈帧包含:局部变量表、操作数栈、动态连接信息、方法正常完成和异常完成信息。
(四).堆
(1).什么是堆?
Java中的堆是JVM所管理的最大的一块内存空间,是一个运行时数据区,通过new等指令创建,不需要程序代码显式释放。主要用于存放各种类的实例对象。在Java中,堆被划分成两个不同的区域:一块是新生代(New Generation), 另一块是老年代(Old Generation)。在New Generation中,又被划分为三个区域:Eden、From Survivor、To Survivor。Eden的空间,主要是用来存放新生的对象,From Survivor Spaces、To Survivor Spaces, 它们用来存放每次垃圾回收后存活下来的对象。
在Old Generation中,主要存放应用程序中生命周期长的内存对象,还有个Permanent Generation,主要用来放JVM自己的反射对象,比如类对象和方法对象等。
在New Generation块中,垃圾回收一般用复制算法,速度快。每次GC的时候,存活下来的对象首先由Eden拷贝到某个Survivor Space, 当Survivor Space空间满了后, 剩下的live对象就被直接拷贝到Old Generation中去。因此,每次GC后,Eden内存块会被清空在Old Generation块中,垃圾回收一般用标记整理的算法,速度慢些,但减少内存要求。
图
(2).堆的主要特点?
堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。缺点是由于要在运行时动态分配内存,存取速度较慢。
(五).程序计数器
(1).什么是程序计数器?
《深入理解Java虚拟机》中是这样解释的:程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。