JVM概述
JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策略,保证了JVM的高效稳定运行。不同的JVM对于内存的划分方式和管理机制存在着部分差异。现在探讨的是经典布局
具体运行时数据区的划分(阿里规范中划分的)
其中方法区和堆是线程共用的,所以也是容易产生线程安全区域,同时也是垃圾回收的重点区域(其实重点区域是堆,很少涉及到方法区的垃圾回收)
程序计数器、虚拟机栈、本地方法栈是线程私有的一个区域,所以不会产生线程安全,他们的生命周期和线程基本一致,所有也不会进行垃圾回收
线程在JVM中的关系
线程是一个程序里的运行单元。JVM允许一个应用有多个线程并行的执行。 在Hotspot JVM里,每个线程都与操作系统的本地线程直接映射。
当一个Java线程准备好执行以后(就是运行时数据区中的资源分配好之后),此时一个操作系统的本地线程也同时创建。Java线程执行终止后,本地线程也会回收。 操作系统负责所有线程的安排调度到任何一个可用的CPU上。一旦本地线程初始化成功,它就会调用Java线程中的run()方法。
如果线程中,某个线程出现了异常,这个时候如果这个线程是最后一个非守护线程,这个时候JVM就会停止运行
常见的JVM系统线程
-
虚拟机线程:
-
这种线程的操作是需要JVM达到安全点才会出现。这些操作必须在不同的线程中发生的原因是他们都需要JVM达到安全点,这样堆才不会变化。
-
这种线程的执行类型包括"stop-the-world"的垃圾收集,线程栈收集,线程挂起以及偏向锁撤销。
-
-
周期任务线程:这种线程是时间周期事件的体现(比如中断),他们一般用于周期性操作的调度执行。
-
GC线程:这种线程对在JVM里不同种类的垃圾收集行为提供了支持。
-
编译线程:这种线程在运行时会将字节码编译成到本地代码。
-
信号调度线程:这种线程接收信号并发送给JVM,在它内部通过调用适当的方法进行处理。
内存中的栈与堆
栈是运行时的单位,而堆是存储的单位
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。
堆解决的是数据存储的问题,即数据怎么放,放哪里
局部变量的基本数据类型放到栈中,其他的一般都会放到堆中
引用数据类型,堆中存储数据,栈中存储堆中地址
程序计数器(PC寄存器)
JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟,在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致
PC寄存器用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令(由执行引擎修改PC寄存器的值)。宏观上来看就是记录程序该运行哪行代码
任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行当前方法的JVM指令地址;如果是在执行native(本地)方法,则是未指定值(undefined)
字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
它是唯一一个在Java虚拟机规范中没有规定任何OutofMemoryError(OOM)情况的区域
为什么要将PC寄存器设置为私有的
多线程在一个特定的时间段内只会执行其中某一个线程的方法,CPU会不停地做任务切换,这样必然导致经常中断或恢复,为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个PC寄存器
总结
程序计数器其实就是存储这个线程将要执行的下一个指令的,执行引擎从程序计数器中读取指令,然后执行我们的Java程序