(1)基本概念
jvm是运行在操作系统上可运行java代码的虚拟计算机,它包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收、堆和一个存储方法域。
(2)运行的过程
1.java代码-->编译器-->.class字节码文件
2.字节码文件-->JVM-->机器码
每个操作系统平台的解释器不同,但JVM虚拟机是相同的,这也是java能跨平台的原因。当一个程序开始运行,虚拟机开始实例化,多个程序启动
会存在多个虚拟机。程序退出和关闭,虚拟机实例消亡,虚拟机实例之间数据不能共享。
JVM允许一个程序并发执行多个线程。hotspot JVM(JVM是虚拟机的规范,HotSpot是jvm的具体实现)中的Java线程与原生操作系统的线程是直接映射关系。
当线程本地存储、缓冲区分配、同步对象、栈、程序计数器等准备以后,就会创建一个操作系统原生线程。Java线程结束,原生线程随之被回收。操作系统负责调度
所有线程,并分配到可用的cup上。原生线程初始化完毕后,就会调用Java线程的run方法。线程结束,就会释放原生线程和Java的线程的所有资源。
hotspot JVM 后台运行的系统线程主要有以下几个:
虚拟机线程:这种线程的操作需要JVM达到安全点才出现,这些操作必须在不同线程中发生的原因是他们都需要JVM达到安全点,这样堆才不会变化。
这种线程的执行类型包括“stop-the-world”的垃圾收集、线程栈收集、线程挂起以及偏向锁撤销。
周期任务线程:这种线程是周期事件的体现(比如中断),他们一般用于周期性操作的调度执行。
GC线程:这种线程对在JVM里不同种类的垃圾收集行为提供支持。
编译线程:在运行时会将字节码编译成机器码。
信号调度线程:这种线程接收信号并发给JVM,在它内部通过调用适当JVM方法进行处理。
JVM 内存区域
JVM 内存区域主要分为私有区域【程序计数器、虚拟机栈、本地方法栈】,共享区域【Java堆、方法区】、直接内存。(见图【来自csdn】)
程序计数器(线程私有)
这是一块比较小的内存空间(在虚拟机中没有任何的OutOfMemoryError的区域),是当前线程所执行的字节码的行号指示器,每个线程都有一个独立的
程序计数器,这类内存称为”线程私有“的内存。执行Java方法的话,计数器记录的就是虚拟机字节码指令的地址(当前指令的地址),如果是Native方法,则为空。
虚拟机栈(线程私有)
是描述Java方法执行的内存模型,Java方法执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个
方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入出栈的过程。
栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态连接(Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)。栈帧
随着方法调用而创建,随着方法结束而摧毁------无论方法是正常完成还是异常完成(抛出了在方法内为被捕获的异常)都算作方法结束。
本地方法区(线程私有)
本地方法区和Java虚拟机栈作用类似,区别是虚拟机栈是为执行Java方法服务,而本地方法栈则为Native方法(C或C++编写的方法,一般用来调用底层硬件)服务,HotSpot JVM
直接把本地方法栈和虚拟机栈合二为一。
堆(Heap-线程共享)- 运行时数据区
该区域是线程共享的,创建的对象和数组都保存在Java堆内存中,也是垃圾收集器进行垃圾收集的重要的内存区域。新一代的JVM采用分代收集算法,因此Java堆从GC角度可以细分为
新生代(Eden区、SurvivorFrom区和 SurvivorTo区)和老年代。
新生代:用来存放新生的对象,大概占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁的触发MinorGC进行垃圾回收。新生代又分为Eden区、SurvivorFrom区 、SurvivorTo区。
Eden区:java新对象的出生地,占新生代内存的8/10(如果新创建的对象占用内存很大,则直接分配到老年代),如果Eden区的内存不够时就会接触MinorGC,对新生代区进行一次垃圾回收。
SurvivorFrom区:占新生代内存的1/10,上一次MinorGC幸存者,作为这次MinorGC的被扫描者。
SurvivorTo区:占新生代内存的1/10,MinnorGC扫描SurvivorFrom区之后的幸存者,大部分是还有引用的对象。
老年代:大概占堆内存的2/3,主要存放应用程序中生命周期比较长的内存对象。老年代的对象比较稳定,MajorGC不会频繁的执行。在进行MajorGC之前一般都要先进行一次MinorGC,新生代的对象
进入老年代,只有老年代的空间不足时才会触发。不能找到足够大的连续空间分配给新创建的大对象时也会触发一次MajorGC进行垃圾回收。
方法区/永久代(线程共享)
方法区/永久代用于存储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。HotSpot JVM把GC分代收集扩展至方法区(和Java堆一样管理这部分内存),方法区/永久代
的内存回收的主要目标是针对常量池的回收和类型的卸载。
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期
生成的各种字面量和符号引用,这部分内容在类加载后存放到方法区的运行时常量池中。
JAVA8和元数据
在Java8中,永久代被移除,被“元数据区”(元空间)所取代。元空间的本质和永久代类似,它俩的最大区别:元空间并不在虚拟机中,而是使用本地内存(即元空间内存受本地内存限制)。类的
元数据放入本地内存中,字符串池和类的静态变量放入Java的堆中。