JVM,Java虚拟机,是Java实现跨平台运行的核心组件,也是面试必考的知识点,本篇博文全篇默认标记为重要。
介绍下Java内存区域(运行时数据区) / 介绍下JVM内存模型
Java虚拟机在执行Java程序的过程中会把它管理的内存划分成若干个不同的数据区域。JDK1.8和之前的版本有所不同。
JDK1.8之前:
JDK1.8之后:(即原方法区从运行时数据区移到直接内存中的元空间)。
线程私有的:
- 程序计数器
- 虚拟机栈
- 本地方法栈
线程共享的:
- 堆
- 方法区
- 直接内存(非允许时数据区的一部分)
程序计数器
程序计数器是一块较小的内存空间可以看作当前线程所执行的字节码的行号指示器。其主要功能概括为如下:
- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如顺序执行、选择、循环、异常处理。
- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候知道该线程上次运行到哪里。
程序计数器是唯一一个不会出现OOM
的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
Java虚拟机栈
同程序计数器,Java虚拟机也是线程私有的,它的生命周期和线程相同,描述的是Java方法执行的内存模型,每次调用的方法都是通过栈传递的。
Java内存可以粗糙的区分为堆(Heap)内存和栈(Stack)内存,其中栈就是现在说的虚拟机栈,或者说虚拟机栈中的局部变量表。(Java虚拟机栈由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口等信息。)
Java虚拟机栈出抛出两种异常:StackOverflowError
和OutOfMemoryError
。
StackOverFlowError
:若Java虚拟机的内存大小不允许动态扩展,那当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就会抛出StackOverFlow
。OutOfMemoryError
:若Java虚拟机的内存大小允许动态扩展,且线程请求栈时内存用完了,无法再动态扩展了,此时抛出OutOfMemoryError异常。
每一次方法调用都会有一个对应的栈帧被压入虚拟机栈,每一次方法调用结束(return或者抛出异常),都会有一个栈帧被弹出。
本地方法栈
作用与虚拟机栈非常相似,区别在于:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用的Native方法服务。在HotSpot虚拟机本地方法栈和Java虚拟机栈合二为一。
本地方法在执行的时候,在本地方法栈也会创建一个栈帧。用于存放本地方法的局部变量表、操作数栈、动态链接、出口信息(同虚拟机栈)。
方法执行完毕之后相应的栈帧也会被弹出并释放空间,也会出现StackOverFlow和OutOfMemoryError两种异常。
堆
堆区是java虚拟机所管理内存中最大的一块,Java堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域唯一的目的就是存放对象实例,几乎所有对象实例以及数组都在这里分配内存。
Java堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap)。从垃圾回收的角度,由于现在收集器基本采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代:在细致一点:Eden空间、From Survivor、To Survivor空间等。进一步划分的目的是更好地回收内存或者更快地分配内存。
方法区
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。尽管Java虚拟机规范将方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-heap,目的是与Java堆区区分开。
参考
Java Guide面试突击版,百度可得最新版本,这里可能有删减,增加和修正。