JVM简介
Java Virtual Machine(Java虚拟机),它是一个虚拟出来的计算机,是通过在实际的计算机仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关信息,使得Java程序只需要生成在Java虚拟机上运行的字节码,就可以在多种平台不加修改的运行。Java虚拟机在执行字节码时,实际上最终还是把字节码解释为具体平台的机器码指令。
JVM 内存模型
虚拟机栈
虚拟机栈是线程私有的内存空间,创建一个线程就会在虚拟机栈中获取一个线程栈,线程栈有多个栈帧,一个栈帧保存方法的局部变量表、操作数栈、动态链接、方法出口等信息。
若单个线程请求的栈深度大于虚拟机允许的深度,则会抛出StackOverflowError(栈溢出错误)。
不同于StackOverflowError,OutOfMemoryError指的是当整个虚拟机栈内存耗尽,并且无法再申请到新的内存时抛出的异常。
本地方法栈
本地方法栈与虚拟机栈类似,虚拟机栈执行的是Java方法服务,本地方法栈执行的是本地方法服务(用native关键字修饰的方法)。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。
堆
堆(Heap),一个JVM只有一个堆内存,堆内存的大小是可以调节的。该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
程序计数器
程序计数器(Program Counter Register)用来储存指向下一条字节码指令的地址,也就是即将要执行的字节码指令,由存储引擎读取下一条指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。在JVM规范中,每个线程都有自己私有的程序计数器,生命周期与线程的生命周期保持一致。
方法区
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。方法区是一种规范,不同的虚拟机厂商可以基于规范做出不同的实现,永久代和元空间就是出于不同jdk版本的实现。说白了,方法区就像是一个接口,永久代与元空间分别是两个不同的实现类而已。只不过永久代是这个接口最初的实现类,后来这个接口一直进行变更,直到最后彻底废弃这个实现类,由新实现类——元空间进行替代。
类加载子系统
类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
类的生命周期
类的生命周期包括,加载、连接(包含验证、准备、初始化)、初始化、使用和卸载。