一 、概述
在学习jvm相关的知识的时候,总能看到一些专业名词就学科概念,本文整理了一些基础知识,为后面的学习提供基础帮助。
二、基础概念
2.1 类加载器
在程序执行时,需要将本地磁盘的字节码文件加载到内存中供机器执行,这类的工作由类加载器完成。
类加载器分类:
- 启动类加载器(BootstrapClassLoader):
使用C++实现(仅限于HotSpot),是虚拟机自身的一部分。负责将存放在\lib目录中的类库加载到虚拟机中。其无法被Java程序直接引用。 - 扩展类加载器(ExtentionClassLoader):
由ExtClassLoader实现,负责加载\lib\ext目录中的所有类库,开发者可以直接使用。 - 应用程序类加载器(ApplicationClassLoader):
由APPClassLoader实现。负责加载用户类路径(ClassPath)上所指定的类库。
2.2 即时编译
在程序的执行过程中能够随着功能的需要,及时将字节码文件编译成机器码的过程称为即时编译。
JIT编译器(Just In Time Compiler)
在java虚拟机中,通常采用的模式是一边解析一边执行代码的,为了提升效率会在执行的代码的同时去标记哪些是热点代码(通常用执行频率来判断),对于这些热点代码虚拟机会进行层层优化将其编译成与当前平台和机器硬件相关的机器码写入磁盘,并将这些调用方法的调用地址指向保存机器码的磁盘地址。而用于编译这些热点代码的编译称之为 即时编译器。
2.3 双亲委派机制
双亲委派机制是描述java类加载的一个过程,我们编写的可执行代码在编译成字节码之后,在执行过程中需要被类加载器加载。通常由App ClassLoader 进行加载。在加入加载时,App ClassLoader 会先读取缓存,如果缓存不存在则会委托 Extension ClassLoader 加载, Extension ClassLoader加载时 同样会去检查缓存,如果缓存不存在。 Extension ClassLoader 又会委托 Bootstrap ClassLoader进行加载,Bootstrap ClassLoader检查缓存是否存在,不存在则会在其指定的路径搜索,不存在的话则抛出 ClassNotFound Exception,Extension ClassLoader捕获到异常,则会在其类路径 /lib/ext 进行搜索加载,不存在又会抛出 ClassNotFound Exception,App ClassLoader 捕获到异常又开始在 classPath 路径下进行搜索加载,不存在的话继续抛出 ClassNotFound Exception。
2.4 对象的引用级别
1、强引用 Strong Reference
强引用是在开发过程中使用最普遍的引用。当内存空间不足时,Java虚拟机也不会强制收回,只会抛出OutOfMemoryError错误,使程序异常终止。我们经常使用的 new 来创建的对象都是强引用,除此之外还有给String 对象赋值的操作。
Student student=new Student();
String name= “xiaoming”
在ArrayList类中定义了一个私有的变量elementData数组,在调用方法清空数组时可以看到为每个数组内容赋值为null。不同于elementData=null,强引用仍然存在,避免在后续调用 add()等方法添加元素时进行重新的内存分配。使用如clear()方法中释放内存的方法对数组中存放的引用类型特别适用,这样就可以及时释放内存。
2、软引用 Soft Reference
软引用跟强引用不同,使用软引用时当内存不足的时候,java虚拟机会将这些对象回收释放内存。通常用于一些使用完可以直接被回收的场景中,它可以跟软引用队列结合一起使用 SoftReference。
3、弱引用 Weak Reference
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
4、虚引用 Phantom Reference
与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。
2.4 STW
stop the world指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应, 有点像卡死的感觉,这个停顿称为STW。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;
2.5 TLAB
TLAB (Thread Local Allocation Buffer,线程本地分配缓冲区)是 Java 中内存分配的一个概念,它是在 Java 堆中划分出来的针对每个线程的内存区域,专门在该区域为该线程创建的对象分配内存。它的主要目的是在多线程并发环境下需要进行内存分配的时候,减少线程之间对于内存分配区域的竞争,加速内存分配的速度。TLAB 本质上还是在 Java 堆中的,因此在 TLAB 区域的对象,也可以被其他线程访问。
三、JVM内存模型的组成
jvm的组成将其分为两个部分,按照线程共享和线程私有分类来讲解
3.1线程私有
3.1.1 虚拟机栈
-
局部变量表
用于存储方法定义返回值或者方法内定义的局部变量,分为基础类型和自定义类型,基础类型存储的是值,自定义类型存储其对象的内存空间地址。 -
操作数栈
操作数栈主要用于保存运算过程中的中间结果,同时作为计算过程中变量的临时的存储空间.操作数栈就是JVM执行引擎的一个工作区。 -
动态链接
在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。 -
方法出口
用于记录方法执行结束时的出栈地址,如果正常执行则是返回值地址,如果发生异常则返回报错异常地址。
3.1.2 本地方法栈
本地方法栈是为虚拟机的Navtive 提供服务了,作为java调用本地方法的抽象层。
3.1.3 程序计数器
用于记录当前线程的操作地址,作用是当CPU暂停该线程执行后,再次执行时可以继续为完成的操作。
3.2 线程共享
3.2.1 元数据空间
元数据空间是用来存储各种类信息的一块本地内存空间,它由方法区和运行时常量池组成。
-
类信息
它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。 -
运行时常量池
用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
运行时常量池,在加载类和接口到虚拟机后,就会创建对应的运行时常量池。
3.2.2 堆空间
java堆是jvm 内存最大的一块区域,所有的对象实例以及数组都在运行时分配在堆上,这块区域也是GC操作的对象。为了优化GC的性能和策略,java堆还分了年轻代(YoungGen)和老年代(oldGen)。
-
3.2.2.1 年轻代(YoungGen)
年轻代 顾名思义 就是用于存放刚创建不久的对象的一块区域,它可再细分为:
Eden空间、Survivor0空间和Survivor1空间(有时也叫做from区、to区)。 -
Eden空间: 对象刚被创建时就会被存放在这个区域。
-
Survivor0空间:当Eden空间空间被占满,发生GC后,还被引用的对象,将会进入该区域。
-
Survivor1空间:当Eden空间或者Survivor0空间被占满,发生GC后,还被引用的对象,将会进入该区域。
-
3.2.2.2 老年代(oldGen)
老年代即存放经过了多轮GC 之后还被引用的对象的空间。