G1垃圾收集器回收步骤
1:类加载机制:
加、验、准、解、初、使、卸
加、烟、准、姐、初、湿、鞋
1.1:加载、将class 文件转化为二进制流加载 JVM 内存中并生成一个该类的Class对象
1.2:验证、Class 文件的字节流中包含的信息是否符合当前虚拟机的要求
1.3:准备、在方法区中分配这些类变量所使用的内存空间
1.4:解析、虚拟机将常量池中的符号引用替换为直接引用的过程
1.5:初始化、执行类构造器<client>方法,编译器收集类变量的赋值操作和静态语句块中的语句,
虚拟机会确保父类的<client>方法已经执行完毕,如果没有静态变量和静态语句则
不生成client方法
2:引用:
强、软、弱、虚/从不/不足/GC后/跟踪回收状态
3:类加载器:
引导类加载器、扩展类加载器、应用程序类加载器
4:双亲委派机制:
解释:自己不加载委派给父类去完成,如果无法加载则自己再加载,
好处:使用不同的类加载器最终得到的都是同样一个 Object 对象
5:JVM:
5.1:1.7-堆、栈、方法区、程序计数器、本地方法栈
1.8-堆、栈、元空间(本地内存)、程序计数器、本地方法栈
默认堆大小:-Xms:1/64,-Xmx:1/4
新生代默认:-NewRatio:2,老年代/新生代的值= 2
新生代和幸存者区比例:8:1:1
新生代大小:-Xmn
老年代大小:堆总-新生代
6:调优工具:
|命令行|Jconsole|VisualVM|Jprofiler|Java Flight Recorder|GC Viewer|GC Easy|
6:确定垃圾
6.1-引用计数法:为对象添加引用计数器,引用它+1,失效时-1
优点:实现简单,判定效率高,回收没有延时性。
缺点:需要单独的字段存储增加存储开销。
每次赋值增加时间开销。
无法处理循环引用的问题。
6.2-可达性分析法:从GC Roots到这个对象可不可达,
没有finalize()方法,直接干掉,
有的话加入队列再finalize(),有连接则移除,没有则回收。
优点:解决循环依赖问题,防止内存泄露
6.2.1-GC Roots:
6.2.1.1:各线程调用方法的参数,局部变量
6.2.1.2:本地方法栈内的引用对象
6.2.1.3:方法区中静态属性引用对象
6.2.1.4:常量引用的对象
6.2.1.5:锁持有的对象
6.2.2-finalize 方法:销毁之前自己处理逻辑,用于对象回收之前最资源的释放,只能调一次
会导致对象复活
执行时间没有保证,若不发生GC则没机会执行
严重影响性能
6.2.3-对象的三种状态:
可触及
可复活
不可触及
内存泄露:对象不可用并且垃圾收集器无法回收
内存溢出:没有足够的内存,并且垃圾收集器无法提供更多的空间
6:垃圾回收算法
6.1-拷贝、标记清除、标记整理
6.2-分代回收思想
年轻代-拷贝,年龄+1,到15移到老年代
老年代-标记整理
6.3-分区回收思想
Region
7: 垃圾收集器
7.1-年轻代:Serial(串)、 ParNew(并)、 Parallel Scavenge(并多、吞)-1.8默认、
7.2-老年代:Serial Old(串)、CMS(初并重并)、 Parallel Old(并)-1.8默认、
7.3-堆:G1(并发)
7.4-CMS的问题:
7.4.1-占用CPU资源而导致引用程序变慢,总吞吐量下降。默认线程数是:(CPU数量+3) / 4
7.4.2-CMS收集器无法处理浮动垃圾
7.4.3-需要预留空间供并发收集时的程序运作使用,68%时会被激活,
预留空间不够抛异常,启用Serial Old导致停顿时间过长。
7.4.4-标记清除,产生碎片。
G1:
g1在实现高吞吐量的同时,尽可能的满足垃圾收集器的暂停时间要求
内存结构:2048个Region,每个Region 大小在1-32M之间,默认年轻代初始大小整堆的5%,最大整堆的60%
PRT:Per Region Table 是RSet 在内部记录分区的引用情况。
稀少:直接记录引用对象的卡片索引
细粒度:记录引用对象的分区索引
粗粒度:只记录引用情况,每个分区对应一个比特位
SATB:
用于记录某个时间点,每个 Region 中包含的对象及其存活标记信息的快照。
写屏障:
PLAB:
线程在分配内存时,首先尝试从 PLAB 中分配。
如果 PLAB 中的空间不足,线程将开始一个新的 PLAB。
这样能够避免多个线程竞争一个全局分配缓存而导致的性能瓶颈。
G1垃圾回收的理解:
第一阶段
1:对象的分配,修改RSet。
2:年轻代满了触发Young GC
2.1:初次标记、
2.2:在并发标记和最终标记的过程中计算出回收价值最大的Region 放入CSet
2.3: 根据CSet 进行回收,就是所谓筛选回收。如果大对象大多数已死亡也一并回收。
2.4:完成回收后,将活得对象放入幸存者区,对象年龄+1,再次修改RSet
3:有了新的空间用户线程就可以将新的对象放入Eden区了。
第二阶段:
当Young GC 多次后,幸存者区的对象年龄超过15 将进入老年代
第三阶段:
此时应用线程还在运行,继续产生新的对象,PLAB 用于临时存储从 Eden 区域晋升过来的对象。 在执行垃圾回收操作之前,G1 垃圾回收器会将 PLAB 中的对象依次晋升到老年代中,这样可以减少老年代中的内存分配操作,对于每个线程,G1 垃圾回收器都维护了一个或多个 PLAB。线程在分配内存时,首先尝试从 PLAB 中分配。如果 PLAB 中的空间不足,线程将开始一个新的 PLAB。这样能够避免多个线程竞争一个全局分配缓存而导致的性能瓶颈。
第四阶段:
G1 垃圾回收器在执行 Mixed GC 之前会先记录一份 STAB,
记录 Mixed GC 区域中各个 Region 的存活对象信息和存活标记信息。
之后,G1 垃圾回收器会在 Mixed GC 区域中从存活对象开始,
检查每个已存活的对象所在的 Region,并将这些 Region 标记为 "可复制" 的备选区域。
同时,采用各种启发式算法来选择最佳的 Region 备选列表,
从而在 Mixed GC 期间减少空间碎片,提高内存利用率。
第五阶段:
1:老年代存储达到阈值默认是45% 将触发Mixed GC
2:初次标记。
3:在并发标记和最终标记的过程中,计算出回收价值最大的Region(包含所有区的Region)放入CSet。
4: 根据CSet 进行混合回收。如果大对象少数死亡也回收一部分。
5:将存活的对象拷贝到其他的Region。
第六阶段:
1:拷贝过程中如果发现没有足够的空region能够承载拷贝对象就会触发一次Full GC。
2:此时应用程序的执行会完全停止,进行所有区域的标记和维护操作,以获得充足的空间。
使用场景:
50%以上的堆被存活对象占用
对象分配和晋升的速度变化非常大
垃圾回收时间特别长,超过1秒
8GB以上的堆内存(建议值)
停顿时间是500ms以内
优化:
1:增加G1垃圾回收器的堆大小:由于G1垃圾收集器使用了分代技术,
因此需要适当增加堆大小来提高垃圾回收的效率。通常,可以通过设置
JVM 启动参数 -Xmx 和 -Xms来增加堆大小。
2:设置最大G1回收时间:在优化 G1 垃圾回收器时,需要了解每次垃圾回收所需的时间。
可以通过设置 JVM 启动参数-XX:MaxGCPauseMillis来控制最大的垃圾回收时间,
从而提高垃圾回收的效率。
3:调整G1垃圾回收器的分区大小:G1垃圾回收器将堆划分为若干个分区进行垃圾回收。
可以通过设置 JVM 启动参数 -XX:G1HeapRegionSize 来调整分区大小,
从而优化垃圾回收器的性能。
4:增加并发线程数:可以通过增加垃圾回收器的并发线程数来提高其效率。
可以使用 JVM 启动参数 -XX:ConcGCThreads 来增加并发线程数,
从而提高垃圾回收器的并发性能。
5:避免使用超大对象:G1垃圾回收器的使用应避免创建超大对象。
超大对象容易导致分配失败、GC 时间过长等问题。