jvm
2023-02-08 12:07:35 0 举报
AI智能生成
jvm的脑图, 覆盖类加载机制, 内存模型, 判断垃圾, 垃圾回收算法, 垃圾回收器
作者其他创作
大纲/内容
7.GC日志查看
6.垃圾回收器
G1
- 将Java堆拆分成多个大小相等的Region, JVM最多可以用2048个Region, Region的大小必须是2的倍数, 比如4G 4096/2048=2M(每个Region),可以手动设置 "-XX: G1HeapRegionSize"
2.最大的特点是让我们设置一个垃圾回收预期停顿时间
比如追踪发现, 1个Region中的垃圾对象有10MB, 回收耗时1s,另一个Region有20MB,回收耗时200ms
G1也有新生代和老年代的概念, 只不过是逻辑上的概念
新生代也有Eden和Survivor的概念吗?
之前有一个新生代的参数 "-XX:SurvivorRatio=8", 所以这里还是区分新生代的Region那些是属于Eden和Survivor,
比如新生代有100个Region, 那么可能有80个Region是Eden, 2个Survivor各自占10个Region
比如新生代有100个Region, 那么可能有80个Region是Eden, 2个Survivor各自占10个Region
简单总结:
G1可以做到让你来设定垃圾回收对系统的影响, 他通过内存拆分成大量小Region, 一级追踪每个Region可以回收对象大小和预估时间,最后在垃圾回收的时候, 尽量把垃圾回收堆系统的影响控制在你指定的范围内, 同时在有限的时间内回收尽可能多的对象
- 回收触发时机-新生代
新生代
刚开始新生代对堆内存占比是5%, 比如4G, 那么新生代就是200MB,对应100个Region, 可以通过"-XX:G1NewSizePercent"设置比例, 随着系统运行, jvm会不停给新生代增加Region, 但不会超过60%, 可以通过"-XX:G1MaxNewSizePercent"
一旦新生代达到了设定的占据堆内存大小60%, 比如1200个Region, Eden可能占据了1000个Region, 每个survivor是100个Region,而Eden还占满了对象, 这个时候还是会触发新生代的GC, G1就会用之前说过的复制算法进行垃圾回收, 进入STW状态,
也不一定非要到60%才会触发这个新生代gc,
可能回收100个Region耗时达到设置的停顿时间也有可能触发新生代gc
可能回收100个Region耗时达到设置的停顿时间也有可能触发新生代gc
老年代
什么时候触发新生代和老年代的混合回收(老年代的回收方式:混合回收)
G1 有个参数"-XX:InitiatingHeapOccupancyPercent"的默认值是45%, 意思是如果老年代占据堆内存的45%Region时候, 此时就会尝试出发新生代和老年代的混合回收阶段; 比如堆内存有2048个Region,老年代占据其中45%(接近1000个) 就会出发一个混合回收
大对象也是在这个阶段回收
大对象也是在这个阶段回收
垃圾回收过程
初始标记: 仅仅是标记一些Gc Roots直接引用对象,速度很快会STW
并发标记: 这个阶段允许系统的运行, 同时Gc Roots跟踪,从Gc roots开始追踪所有存活对象,这个还是很耗时, 因为要追踪全部的存活对象, 但是这个阶段是并发进行的,所以对系统活动影响不大
最终标记: 根据并发标记阶段记录的那些对象修改, 最终标记一下有那些存活对象, 有那些是垃圾对象, 会STW
混合回收: 这个阶段会计算老年代每个Region中存活对象, 存活对象的占比,还有预期回收性能和效率,接着停顿系统程序,
然后全力衣服尽快垃圾回收,控制好停顿时间在指定范围内
然后全力衣服尽快垃圾回收,控制好停顿时间在指定范围内
比如老年代此时1000个Region都满了,但根据设定的目标,本次垃圾回收可能只能停顿200ms,
那么通过之前的计算得到,可能回收800个Region刚好需要200ms, 那么只能回收800个Region,
把gc导致停顿时间控制在我们指定的范围内
那么通过之前的计算得到,可能回收800个Region刚好需要200ms, 那么只能回收800个Region,
把gc导致停顿时间控制在我们指定的范围内
"-XXGxMixedGCCountTarget"
指一次混合回收过程中, 最后一个阶段执行几次垃圾混合回收,默认是8次, 意味这个阶段,
先停止系统运行, 混合回收部分region,恢复运行, 再停止系统运行, 混合回收region;
比如800region,一次回收100个,反复8次
为什么要反复多次呢? 为了达到最少停顿时间的要求
先停止系统运行, 混合回收部分region,恢复运行, 再停止系统运行, 混合回收region;
比如800region,一次回收100个,反复8次
为什么要反复多次呢? 为了达到最少停顿时间的要求
"-XX:G1HeapWastePercent"默认值5%
Mixed GC过程中, 对Region回收都是基于复制算法进行的, 都是把要回收的Region里的存活对象放入其他Region, 然后这个Region中的垃圾全部清理掉, 这个过程中不断有空出来的心的Region, 一旦这些空出来的Region数量达到堆内存的5%, 本次混合回收就结束了
"-XX:G1MinEDGCLiveThresholdPercent" 默认值85%
确定要回收region时候,必须是存活对象低于85%的region才可以回收,否则要是一个region的存活对象多余85%, 你还可以回收他干嘛呢?
拷贝85%的对象到别的region, 成本是很高
拷贝85%的对象到别的region, 成本是很高
混合回收失败的时候Full GC
万一出现拷贝过程中发现没有空闲的region可以承载自己存活对象了, 就会触发一次失败, 一旦失败立马切换成停止系统程序, 然后采取单线程进行标记,清理和压缩整理, 空出来一个region, 这个过程是极慢极慢的
Serial(串行收集器)
ParNew
Paralel Scavenge
Serial Old
Paralel Old
CMS
5.垃圾回收算法
分代回收
新生代
复制/清除
老年代
标记整理算法
什么情况下会进入老年代(专栏17章详细说明)
1.躲过15次垃圾回收(-XX:MaxTenuringTHreshold)来设置, 默认15岁
2.动态年龄判断
年龄1+年龄2+年龄n对象内存总和超过survivor区50%, 此时会把年龄n以上的对象都放到老年代, 这么做为了让有可能长期存活的对象尽早进入老年代
3.大对象直接进入老年代
"-XX:PretenureSizeThreshold", 可以把他的值设置成字节数, 比如"1048575", 就是1MB
4.minor gc导致进入老年代
5.老年代空间担保规则
https://www.processon.com/diagraming/63e30ad36330b9282f6f9f74
1.class是怎么加载JVM的?? 类加载机制
类加载机制
1. 加载
1.通过类的权限定名获取类的二进制字节流
2. 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
3. 内存中生成该类的java.lang.Class对象作为方法区这个类的各种数据的访问入口
2. 链接
验证
文件格式验证
元数据验证, 是否符合Java语言规范
字节码验证, 确保程序语义
符号引用验证
准备
为静态变量再方法区分内存, 并设置默认值
解析
虚拟机将常量池内符号引用替换为直接引用
3. 初始化
标记为常量的字段赋值的过程, 值对static修饰的变量火语法快初始化
什么时候初始化?
1. new该类初始化对象时候
2. 读取或设置静态字段的时候(但被final修饰字段, 在编译时就被放入常量池; 比如static final int a = 1;
3. 调用该类静态方法
4. 发射Class.forName的时候
5. 初始化一个类的时候, 有父类, 先初始化父类(注意1.接口除外,父接口在调用的时候才会初始化 2子类引用父类的静态字段,只会引发父类初始化)
6.被表明是启动类(包含main()的类)要初始化
7.使用jdk1.7的静态语言支持时,如果一个java.invoke.MethodHandler实例自后的解析结果REF_getStatic, REF_putStatic, REF_invokeStatic的方法句柄, 并且这个方法句柄所对应的类没有进行初始化, 则需要先触发初始化
初始化顺序
1. 父类的静态变量和静态块赋值(按照声明顺序)
2. 自身的静态变量和静态块赋值(按照声明顺序)
3. 父类成员变量和块赋值(按声明顺序)
4. 父类构造方法赋值
如果父类包含有参构造器, 则没无参构造器, 子类一定要super指定调用父类的有参构造器,不然会报错
5. 自身成员变量和块赋值
6. 自身构造器赋值
类加载器
Java提供的
启动类加载器(Boostrap ClasLoader): 最顶层的类加载器, 负责加载java_home\lib目录, 或者-XBotclasspath参数指定路径的类(比如rt.jar)
拓展类加载器(Extendsion ClassLoader): 负责加载java_home/lib/ext目录或者通过java.ext.dirs系统变量指定路径的类库
应用程序类加载器(Application ClassCloader): 也叫系统类加载器, 可以通过getSystemClassLoader()获取, 负责加载用户路径classpath上的类库, 如果没有自定义类加载器, 一般这个是默认的类加载器
tomcat的classLoader
StandardClassLoader负责加载tomcat容器相关的类
WebappClassLoader: 每个web项目对应一个, 因为每个项目中都会有相关的类(package+classname)而类内容不一样, 这样每个项目一个WebappclassLoader达到隔绝项目类冲突的结果
https://www.processon.com/diagraming/63e220cc56e18032d4ad22df
双亲委派模型
如果一个类接受到类加载请求,他自己不会去加载这个请求, 而是将请求委派给父加载器, 这样一层一层传送, 直到到达启动类加载器(Bootstrap ClassLoader), 只有当父类加载器无法加载这个请求时 子加载器才会尝试自己去加载
2.内存模型
1.方法区: Metaspace
2. 字节码执行引擎
3. 本地方法栈
4. 虚拟机栈
5. 程序计数器
6. 堆内存
7. 堆外内存
8. 对象组成
对象头
markword
classpoint(指向类的指针)
数组长度(对象数组会有这个)
实例数据
对齐填充字节
3.怎么判断是垃圾
1. 定义: 没有被引用的可当成垃圾
2. 引用类型
强引用
弱引用
弱引用和软引用的区别: 只具有弱引用的对象拥有更短暂的生命周期. 在垃圾回收thread扫描它所管辖的内存区域过程中, 一旦发现了只具有弱引用的对象, 不管当前内存空间是否足够,, 都回收
软引用
如果内存足够, 垃圾回收期就不会回收ta, 如果内存不足了, 就会回收这些对象的内存. 只要垃圾回收器没有回收它, 该对象就可以被程序使用. 软引用可以用来实现内存敏感的高速缓存
虚引用
根据可达性分析法判断对象是否可以被回收;
简单说没有被GC Roots引用的对象可以被回收
简单说没有被GC Roots引用的对象可以被回收
Gc Roots有哪些?
虚拟机栈(栈帧中本地变量表)引用的对象
方法区静态属性引用的对象
方法区常量引用的对象
本地方法栈JNI(一般说的native方法)的引用对象
如果是软/弱引用, 即使是被GC Roots引用也有可能被回收调
4.对象在内存中是如何流转的
1.对象优先分配在新生代
2. 如果新生代满了, 出发minor gc, 回收掉无人引用的垃圾对象
3.如果对象躲过10次垃圾对象回收, 就会放到老年代里
4.如果老年代也满了, 触发FullGc把老年代无人引用垃圾对象清理掉
0 条评论
下一页