JVM
内存模型
- 堆
- 所有线程共享
- gc回收的区域
- 年轻代老年代:1:4(1:3);年轻代eden:surviorfrom:surviorto = 8:1:1
- 何时要minior GC 何时要 Major/FullGC呢?
- (允许担保的政策)只要老年代的连续空间 > 新生代对象的总大小 或者 历次晋升老年代的平均大小小于老年代的连续空间 则进行miniorGC 否则 就执行 Major 或者 FullGC
- fullgc:
- 代码中执行system.gc
- 老年代空间不足
- 空间担保失败(空间担保:历次晋升老年代的大小小于老年代连续空间)
- 如何区分major,minorCG和fullGc
- Minor GC(小型垃圾回收):
- 通常发生在年轻代(Young Generation)的内存区域。目标是回收年轻代中的垃圾对象。
- 通常是一种快速的垃圾回收过程,会引起短暂的暂停时间。
- 当年轻代空间不足时,将会触发Minor GC。
- Major GC(中型垃圾回收):
- 也被称为Full GC的前奏。
- 通常发生在老年代(Old Generation)的内存区域。目标是回收老年代中的垃圾对象。
- 通常伴随着较长的暂停时间,因为涉及到更多的对象和更大的内存区域。
- Full GC(完全垃圾回收):
- 也称为Major GC。
- 发生在整个堆内存(包括年轻代和老年代)。
- 目标是回收整个堆内存中的垃圾对象,包括年轻代和老年代的对象。
- 通常会导致较长的暂停时间,因为需要清理大量的对象和内存空间。
- 在Java虚拟机中,Major GC(中型垃圾回收)和Full GC(完全垃圾回收)是指的同一个过程。这两个术语是可以互换使用的,它们都表示一种垃圾回收过程,目标是回收整个堆内存(包括年轻代和老年代)的垃圾对象。
- Minor GC(小型垃圾回收):
- java虚拟机栈
- 由栈针组成;栈贞:局部变量;操作数栈;动态链接方法;返回地址
- 创建一个线程就会有一个对应的虚拟机栈,每调用一个方法就会生成一个栈帧
- StackOverFlowError:线程申请的栈超过最大的栈深度 -例如死递归
- 本地方发栈
- JAVA虚拟机栈执行的是JAVA的方法,而本地方法栈则是为虚拟机使用的native方法服务
- 方法区
- 方法区存放已经被虚拟机加载的类信息,字段信息和方法信息,方法区用于存储元数据信息
- 和堆一样是线程共享的 虚拟机中只有一个方法区
- 基本不回收(除非)
- 该类的所有实例都被回收
- 加载该类得类加载器ClassLoder已经被回收
- 类对应的java.Lang.Class对象没有任何地方被引用;无法被反射到
- 程序计数器
- 分支,循环,跳转,异常处理等都需要依赖此计数器完成
- 每一个线程都有一个独立的程序计数器,这种计数器存在线程的私有内存中
- 如果线程正在执行的是一个Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址
- 此内存区域是唯一一个在Java 虚拟机规范中没有规定任何OutOfMemoryError情况的区域
类加载
何时进行类加载
- 实例化对象(new)
- 读取/设置静态字段
- 调用一个类的静态字段
- java.lang.reflect 进行反射的时候
类加载流程
- 加载
- 通过类的全限定名来获取此类的二进制字节流
- 验证
- 确保class文件的字节流中的包含的信息符合当前虚拟机的要求
- 文件格式(coffeebaby开头)原数据验证(得有object类)字节码验证(分析程序语意)
- 准备
- 此阶段为正式为类变量(static)分配内存并设置类变量初始值的阶段
- final直接赋予🈯值
- 解析
- 目的将jvm常量池内的符号引用替换为直接引用的过程
- 初始化
- 初始化阶段就是执行类构造器<clinit>方法的过程;
- 虚拟机会保证一个类的<clinit>()在多线程中正确的加锁同步等
- jvm保证在字类的<clinit>()方法被调用之前,父类的<clinit>()一定会先被调用
- 静态语句块中只能访问到定义在他之前的变量;在其之后的变量,静态语句块可以赋值但是不能访问
- <clinit>()对于一个类或者接口来说并不是必须的,如果类中没有静态语句块也没有对变量的赋值工作,那编译器就不会为该类自动生成一个<clinit>()
类加载器
双亲委派 BootStrap extention application user
对象回收方式
1.8
1.8的GC工具:Parallel Scavenge(新生代)+ Parallel Old(老年代)吞吐量,主要在意cpu执行时间与卡顿时间的比例 注意学习CMS 记住初始标记stw, 并发标记,重新标记stw,并发清理
// 上面是年轻代的gc机制 
Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。
CMS等垃圾收集器的关注点更多的是用户线 程的停顿时间(提高用户体验);
高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适 用于在后台运算而不需要太多交互的任务。
G1回收方式
简单知道: g1跟cms前几种流程类似:初始标记,并发标记最终标记,筛选回收 不同的是cms是一个中标记清理策略,g1是标记整理清除策略, 不会和cms一样使内存出现大量的不连续空间
CMS
以短暂停为目的(采用标记清除算法) 流程: 初始标记(stw),并发标记,重新标记(stw),并发清理,并发重制
缺点: 1. 对cpu敏感,并发标记阶段虽然和用户线程一起标记作废对象,但是会占用一定的cpu资源,导致系统卡顿 2. 会产生的不连续的内存空间 3. 并发清理会产生有些无用的对象不被处理,在清理阶段,用户线程也在产生垃圾,清理不完全 1. cms对此的应对策略是三色标记法
CMS三色是指黑白灰三色
- 黑色:被root节点遍历到的节点,并且引用该节点的对象也已经遍历完成;黑色不能被指向白色
- 灰色:至少存在一个引用该节点的对象没有被遍历到
- 白色:没有被扫描到的节点,回收时候进行回收的节点
- 流程
- 流程:一开始都是白色;被gcroot扫描就是灰色,再遍历灰色,灰色所有节点扫描玩就是黑色,没被遍历就是白色,重复遍历灰色。最后只有白色和黑色
- 出现漏标和错标问题
- 漏标,导致数据回收失败下次才能回收:本来白色节点已经与灰色节点关联了,但是灰色节点与黑色节点的关联被删除了,此时该灰色和白色都应该被删除,但是黑色节点不会重新扫描,而灰色节点扫描了下面的白色节点导致这两个节点不会删除
- 错标,本不应该删除的数据被删除了,黑色节点重新与白色节点进行关联,基于前提黑色节点不会继续扫描,所以上述白色节点本不应该被删除却被删除了
- 为了解决上述问题
- 提供了写屏障及增量更新的方式
- 出现新的节点以及旧的节点被切断的场景,记录下来,等扫描结束以后,对记录的节点重新进行扫描
GCROOT节点
- java虚拟机栈中的引用的对象
- 本地方法栈中的JNI(native方法)引用的对象
- 方法区中的类静态属性引用的对象
- 方法区中的常量引用的对象。
强软弱虚引用
一些简单的jvm参数
-Xms20m :设置jvm初始化堆大小为20m,一般与-Xmx相同避免垃圾回收完成后jvm重新分。 -Xmx20m:设置jvm最大可用内存大小为20m。 -Xmn10m:设置新生代大小为20m。 -Xss128k:设置每个线程的栈大小为128k。
■ -XX:-UseConcMarkSweepGC:使用CMS收集器; 1.8: Parallel Scavenge+Parallel Old。 1.9: g1
G1的流程
rigion,没有明显的区分年轻老年代,每个regin区1~32M,没有太小,如果超过一半的region会将区域标记为H区,超过一个reigen区的用两个region存储并标记为H区 Rset:regien包含Rset,存的是old区谁引用了该reigen,扫描时候可以不用全量扫描, Cset:存储的是需要被回收的对象
初始标记 -- 找到RootRegion区
并发标记 -- 根据RootRegion区的Rset找到对应的old区进行扫描
重新标记 -- 采用SATB的方式标记
筛选回收 --- 这个阶段进行标记整理清楚,并且可以根据用户定义的时间,尽量多的做到对象的回收
safepoint节点:
- 方法调用:在方法调用的入口和出口处,通常都会是安全点
- 循环边界:循环的开始和结束部分通常会是安全点
- 同步点 : 进行同步操作(例如进入 synchronized 块或方法)时,JVM 会在同步点处设置安全点
- 异常处理 : 在异常处理的代码中,也会设置安全点。这样可以保证在异常处理过程中线程能够被暂停,以便进行垃圾回收等操作。
