1 JVM 系统架构图
2 类加载器
几种类加载器
双清委派机制
沙箱安全机制
3 Native
是一个关键字
有声明,没实现 ,c语言
4 PC寄存器
记录了方法之间调用和执行情况,类似排版值日表
用来存储指向下一条指令的地址,也即将要执行的指令
代码,它是当前线程所执行的字节码的行号指示器
5 方法区: 线程共享、存在CG
存放每一个类的结构信息【元数据】(常量池、字段和方法数据、构造函数和普通方法的字节码内容)
方法区是规范,在不同虚拟机实现是不一样的,最典型的是 永久代(1.7)和元空间(1.8)
but
实例对象在堆内存,与方法区无关
6 栈内存
栈管运行、堆管存储
不存在垃圾回收机制
栈保存哪些东西?
8中基本类型的变量
+
对象的引用变量(Person p = new Person() 左边是引用对象在栈里面,右边的实例对象在堆里面)为了保证new出来的person对象,都具有相同的属性和行为信息,所以方法区存了类的结构信息
+
实例方法
方法 = 栈帧 (方法的输入参数、输出参数、和内部变量)
jave.lang.StackOverflowError 栈溢出(没有递归出口的递归调用,一直压栈,栈也是有容量的)
7 堆、栈、方法区的关系
8 堆(*)
逻辑上:
新生区:1.伊甸园区 2.幸存者0区 3.幸存者1区
老年区
元空间
物理上:
新生区 + 老年区
new 对象 静态版本:
伊甸园区有很多new 对象,达到一定的量,会触发YGC
```
```升级版
局部变量表:
静态方法的局部变量表里没有 this 在 index = 0
构造方法和实例方法局部变量表里 有 index = 0 的 this 站位
double 和 float 占两个 slot
可以重复利用slot
int a = 0
{
int b = 0;
b = a + 1
}
// 变量c使用之前已经销的变量b占据的slot的位置
int c = a + 1;
变量的分类
数据类型区分:
基本数据类型
引用数据类型
类声明的位置区分:
成员变量
类变量 (方法之外,static修饰): linkingd的prepare阶段:给类变量默认赋值 ---> initial阶段:给类变量显示赋值即静态代码块赋值
实例变量(方法之外,没有static修饰):随着对象的创建,会在堆空间分组实例变量空间并进行默认赋值
局部变量(方法之内): 在使用前,必须要显示赋值
操作数据栈
指令位置 指令 (
操作数据栈
局部变量表
PC寄存器 (切换指令位置)
心里有个关系图
动态链接
就是栈帧 中字节码 右边的
常量池在 方法区
变量在内部定义且消亡的是线程安全的,如果当初形参和返回出去就不是线程安全了,有可能被其他线程调用和操作,逃逸分析
StringBuilfer 本身是线程不安全的,没有同步机制
StringBuffer 是线程安全的,有同步机制
堆 线程共享
对象加数组的创建int[] myList = new int[10]
堆逻辑上三部分:新生代 老年代 元空间
-Xms10m -Xmx10m 默认(默认四分之一物理机内存 )和最大的堆空间是10MB
建议初始和最大的设置一样的值
新生代: 1.伊甸园区 2.幸存者0区 3.幸存者1区
+
老年代:
= 10MB
jps
jstat -gc pid
或
-XX:+PrintGCDetails
1.java堆区在JVM启动的时候即被创建,其空间大小也就确定了
2.每一个JVM实例只存在一个堆空间
3.栈帧里的局部变量表 维护这 变量名,存的是对象的引用, 指向 堆里的new 对象,
方法区保存的是 这些对象的 类及方法实现的数据信息
一旦 栈帧出栈,指向就没有了,存在堆里的 对象不会马上清除,要等GC操作来决定
tlab
堆是由一个进程的多个线程共享的,存在线程不安全,但是每个线程都会在堆内存开辟属于自己的 tlab 空间,总大小占堆内存的1%
堆的设置参数
逃逸分析
当一个对象在方法中被定义后,对象只在方法内部使用,则没有发生逃逸
当一个对象在方法中被定义后,它被外部方法所引用,则发生逃逸
方法区: 线程共享 类的信息
jinfo -flag MetaspaceSize pid -- 查看 进程对应的元空间大小
-XX:MetaspaceSize=21m
-XX:MaxMetaspaceSize=21m
OOM
内部结构
类信息 + 运行时常量池 + 字符串常量
运行时常量池:
数量值
字符串值
类引用
字段引用
方法引用
垃圾回收
1.什么是垃圾?
运行程序中没有任何指针指向的对象,这个对象就需要被回收
2.垃圾回收相关的算法
GC的阶段:标记阶段:
1.引用计数算法: (java不用,python用)
1.1.只有有任何一个对象引用就加1,引用失效就减1,为0,就可以被回收
1.2.优点:实现简单,效率高
1.3.缺点:无法处理循环引用(对象引用指向线程一个环)的情况,导致内存泄漏
2.可达性分析算法: (java用)
2.1.对象直接或间接的可以和 GC Roots(根对象集合) 连接上,连接不上的是可回收的
2.2.GC Roots 包含哪几类元素呢?
tips: 由于Root采用栈方式存放变量和指针,所以如果一个指针,它保存了堆内存里面的对象,但是
自己又不存放在堆内存里面,那它就是一个Root
清除阶段:1.标记-清除算法(Mark-Sweep)
1.1 当堆中的有效内存空间被耗尽的时候,stop the world (stw)
1.1.1 标记可达对象,递归遍历
1.1.2 回收没有标记为可达对象的对象
1.2 效率不高,需要调整整个应用程序,清理出来的空闲内存不是连续的
2.复制算法
2.1 将可达对象,复制一份变成有序的(幸存者0,1区就是这样实现的)
2.2 优点:效率高,保证空间的连续性
2.3 缺点:需要两倍的内存空间
3.标记-压缩算法(标记整理)
3.1 标记后,进行可达对象的碎片整理
finalize 机制
对象生存还是死亡?三个状态
finalize() 被GC调用,只能被调一次
可触及的 可达对象
可复活的 引用都被释放,但是可能在finalize()中复活
不可触及的 对象的finalize() 被调用,并且没有复活
分代收集算法:具体问题具体分析
年轻代:区域相对老年代较小, 对象生命周期短,存活率低,回收频繁 -- 复制算法是最快的
老年代:区域较大,生命周期长,存活率高,回收不及年轻代平凡,有大量存活率高的对象,复制算法明显不合适
一般由 标记-清除 或 标记-清除和标记-整理混合实现
评估GC的性能指标:吞吐量
吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)
暂停时间
现在的标准:在最大吞吐量优先的情况下,降低停顿时间
java的垃圾回收器有哪些? ***
7款经典收集器与垃圾分代之间的关系
Young Gen Serial GC Parallel Scavenge GC ParNew GC
------------------------------------------------------------------ G1 GC
Old Gen Serial Old GC Parallel old GC CMS GC
.新生代收集器: Serial(串行)、ParNew(并行)、Parallel Scavenge(并行拾荒)
.老年代收集器: Serial Old、Parallel old、CMS
.整堆收集器: G1
注意:
1. jdk8 是 Parallel Scavenge GC 和 Parallel old GC 组合
2. 单cpu clien 串行要好于并行的GC,不用切换线程,吞吐量更大
3. 查看是否使用某个垃圾回收器
jinfo -flag UseParallelGC pid
jinfo -flag UseParallelOldGC pid
***** *****
Serial GC 串行回收
0. 新生代
1. 采用复制算法、串行回收、stop-the-world (stw) 停掉用户线程
2. 优点:运行在client模式下,简单高效
缺点:stw
3. 指定Serial GC : -XX:+UseSerialGC
新生代老年代都是使用的串行回收器
4. java web 应用是不会采用这种串行GC
Serial old GC
0. 老年代
1. 采用标记-压缩算法、串行回收、stop-the-world (stw) 停掉用户线程
***** *****
***** 不太使用了*****
ParNew Serial的多线程版本
0. 新生代
1. 采用复制算法、并行回收、stop-the-world (stw) 停掉用户线程
2. 运行在server模式下
3. 低版本,老年代搭配 Serial old GC
4. 尴尬的定位,高版本没有 老年代的搭档了
***** *****
***** Java 8 默认*****
Parallel Scavenge GC 吞吐量优先
0. 新生代
1. 采用复制算法、并行回收、stop-the-world (stw) 停掉用户线程
2. 可控制的吞吐量
Parallel old GC
0. 老年代
1. 采用标记-压缩算法、并行回收、stop-the-world (stw) 停掉用户线程
参数配置
-XX:+UseParallelGC 手动开启新生代为 并行收集器
-XX:+UseParallelOldGC 手动开启老年代为 并行收集器
以上参数 ,相互生效激活
-XX:ParallelGCThreads 最好与CPU数量相等
-XX:GCTimeRatio 垃圾收集时间占总时间的比例(=1 /(n + 1)) (0-100) 默认n=99 ,垃圾回收时间不超过1%
-XX:+UseAdaptiveSizePolicy 具有自适应调节策略
***** *****
***** JDK 14 被删除 *****
CMS GC 低延迟
0. 老年代
1. 并发收集器,第一次实现让垃圾收集线程与用户线程同时工作
2. 采用标记-清除算法,并且也会 stop-the-world (stw)
3. 适用 看重服务器响应数据的,希望系统停顿时间短的
4. 初始标记(stw)标记GC Roots 直接关联到的对象->并发标记(和用户线程同时工作)遍历整个对象图->重新标记(stw)要停止用户线程去修复->并发清理(和用户线程同时工作)
5. 优点:并发,低延迟
缺点:使用标记-清除算法,就会有内存碎片,占用用户线程,吞吐量降低
为什么不使用标记-压缩算法呢?
因为,标记压缩算法要重新指定对象内存,用户线程就得暂停,出现 stw
4. 只能跟ParNew GC 或 Serial GC 中的一个,原因和其他的底层架构不兼容
5. 提示下,可以在CMS GC 后,对内存进行压缩整理
参数配置
-XX:+UseConcMarkSweepGC 表明老年代使用CMS GC ,新生代默认变成 ParNew GC
-XX:CMSInitiatingOccupanyFraction 设置堆内存使用率的阈值
***** *****
总结:什么情况下使用哪种GC
最小地使用内存和并行开销使用 Serial GC + Serial Old GC
最大应用程序的吞吐量使用Parallel GC + Parallel Old GC
最小化GC的中断时间或停顿时间使用CMS GC + ParNew GC
***** *****
C1 GC 区域化分代式
适用:新生代和老年代
0. 目标:延时可控的情况下,获得尽可能高的吞吐量
1. 特点
1.0.并行与并发
1.1.分代回收, 将堆空间分成若干个区域(Region)
1.2.空间整合, Region之间是复制算法,整体上可以看成 标记-压缩
1.3.可预测的停顿时间模型
1.3.1.由于分区的原因,G1可以只选取部分区域进行内存回收
G1回收器的参数设置
-XX:+UseG1GC 手动指定使用G1收集器
-XX:G1HeapRegionSize 设置每个Region的大小,值是2的幂次方 1 - 32MB
-XX:MaxGCPauseMillis 设置期望到达的最大GC停顿时间
-XX:ParallelGCThread 设置STW时GC线程数的值
-XX:ConcGCThreads 设置并发标记的线程数
-XX:InitiatingHeapOccupancyPercent 设置触发并发GC周期的java堆占用率阈值,默认是45
G1的设计就是简化JVM性能调优
第一步:开始G1垃圾收集器
第二步:开启堆的最大内存
第三步:设置最大的停顿时间
G1中提供了三种垃圾回收模式:YoungGC, Mixed GC 和 Full GC,在不同条件下会触发
***** *****
```