java jvm 介绍
hotspot jvm 学习笔记
首先来看一张图:
类加载机制
-
loading(装载) ;
- 类加载器
Bootstrap:
最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。
Extention:
扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
Appclass:
加载当前应用的classpath的所有类(项目class文件)。
Customer:
用户自定义的类加载器
- 类加载器
-
类加载机制
双亲委派
+ 定义: 如果一个类加载器在接到加载类的请求时,它首先不会自己尝试去加载这个类,而是把这个请求任务委托给父类加载器去完成,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任时,才自己去加载。
+ 好处: 安全. Java类加载自带优先级, Bootstrap > Extention > Appclass > Customer . 比如无论哪个类加载器加载到Object类都会委派给最顶层的Bootstrap Class loader去加载, 不会导致JVM中出现多个Object而引发问题. -
linking(链接)
- 检查-Verification 保证被加载类的正确性
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
- 准备-Preparation 为类的静态变量分配内存,并将其初始化为默认值
例如:
经过此步骤.变量a的值是0而不是10, 真正给变量a赋值为10是在初始化的时候.(不同的数据类型默认值值不同)static int a = 10;
- 解析-Resolution
- Resolution is the process of dynamically determining concrete values from symbolic references in the run-time constant pool.把符号引用(本地变量表(constant pool)中的符号引用)转换为直接引用(直接指向此变量的内存地址).
- 检查-Verification 保证被加载类的正确性
-
initlializing(初始化);
对类的静态变量,静态代码块执行初始化操作
运行时内存管理
-
The pcRegister pc寄存器
在任意时刻,一个处理器只会执行一条线程中的指令。因此,为了线程切换后能够恢复到正确的执行位置,每条线程需要有一个独立的程序计数器(线程私有) . 如果线程正在执行Java方法,则计数器记录的是正在执行的虚拟机字节码指令址;如果正在执行的是Native方法,则这个计数器为空。 -
Java Virtual Machine Stacks 虚拟机栈
虚拟机栈是一个线程执行的区域,保存着一个线程中方法的调用状态. 调用一个方法,就会向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出。- 栈帧
- 局部变量 每个帧都包含一个变量数组,称为局部变量
- 操作数栈
- 动态链接
- 方法返回值
- 栈帧
-
Heap 堆
所有的对象实例以及数组都应当在运行时分配在堆上。因为栈帧中保存的是引用,这个引用指向对象或者数组在堆中的位置 . GC垃圾回收的重点区域- YoungGen 新生代
- Eden 伊甸
- s0
- s1
- OldGen 老年代
- YoungGen 新生代
-
Method Area 方法区
JVM方法区是用于保存已经被虚拟机加载的类元信息(包括类的版本、字段、方法、接口和父类等信息), 加载的类越多, 此空间占用的就越多.
jdk1.7的实现 – 永久代(Permanet Generation space)
jdk1.8的实现 – 元空间(Metaspace) -
Run-Time Constant Pool 运行时常量池
字符串常量、静态变量数据存放区域 , java6中 所有常量池数据是存放在永久代中,但到java7后 Hostpot 把永久代中的字符串常量、静态变量数据迁移到了堆中,后面的java 8并没有对这部分内容进行迁移,在java8 中字符串常量、静态变量数据还是放到堆中,所以常量池只是在JVM规范定义上属于方法区,但Hotspot在实现的时候部分常量池的内容实际上是保存在堆中了 -
Native Method Stacks 本地方法栈
垃圾回收
- 哪些对象会被回收?
垃圾收集器如何确定某个对象是“垃圾”?JAVA通过一系列的 ‘GC Roots’ 的对象作为起始点,从这些节点出发所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连的时候说明对象不可用即垃圾.- 可作为GC Roots节点的对象
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中 JNI(即一般说的 Native 方法) 引用的对象
- 引用类型
- 强引用: 直接 new ,如 new Object(); 只要这类强引用还在,对象就不会回收
- 软引用:(SoftReference类)用来描述一些还有用但非必需的对象;当将来发生内存溢出之前,系统会把这些有软引用的对象列入回收范围中进行二次回收,如果这次回收还没有足够的内存,则内存溢出报异常
- (WakeReference类) 被弱引用的对象只能生存到下一次垃圾收集发生之前,当垃圾收集器开始工作时,无论内存是否足够,都会对这些对象进行回收
- (PhantomReference类) 一个虚引用的对象,它的唯一目的就是能在这个对象被收集器回收时收到一个系统通知,即一个对象是否有虚引用,完全不会对其生存时间构成影响
- 可作为GC Roots节点的对象
- 什么时间回收?
在可达性分析算法不可达的对象,也不一定"非死不可’,它会经历两次标记,一是当不可达 GC Roots 时,标记一次并筛选,筛选的条件是该对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或者已经执行过 finalize 方法时,则认为此对象会被回收. - 如何回收?
- 垃圾回收算法
-
标记-清除算法
标记-清除算法分为两个阶段,标记(mark)和清除(sweep)- 缺点:
1.执行效率不稳定
2,产生大量的碎片(不一定连续的内存地址)
- 缺点:
-
复制算法
复制算法将内存分为两个区间,这两个区间是动态的,在任意一个时间点,所有分配的对象内存只能在其中一个区间(活动区间),另外一个区间就是空闲区间- 优点: 很好地解决了“标记-清除”算法,内存布局混乱的缺点
- 缺点: 浪费一半内存
-
标记-整理算法
- 优点:
- 1、弥补了“标记-清除”算法,内存区域分散的缺点
- 2、弥补了“标记-复制”算法内存减半的代价
- 缺点 : 效率不高
- 优点:
-
分代收集算法
- 1.新生代(Edge)采用复制算法,在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法
- 2.老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须用标记-清除或者标记-整理
-
- 垃圾收集器
- Serial
- 串行收集器使用单个线程来执行所有垃圾收集工作,这使得它相对高效,因为线程之间没有通信开销。
- 开启方法 : -XX:+UseSerialGC
- Parallel
- 并行收集器也称为吞吐量收集器,它是类似于串行收集器的分代收集器。串行收集器和并行收集器之间的主要区别在于并行收集器有多个线程用于加速垃圾收集。
- 开启方法 : -XX:+UseParallelGC
- Garbage-First (G1)
- G1 主要是并发收集器。大多数并发收集器同时对应用程序执行一些昂贵的工作。该收集器旨在从小型机器扩展到具有大量内存的大型多处理器机器。它提供了以高概率满足暂停时间目标的能力,同时实现高吞吐量。
- 开启方法 : -XX:+UseG1GC
- Serial
- 垃圾回收算法