1. 回收算法

2. 回收器

serial适合用于客户端垃圾收集器
parallel scavenge主要适用于在后台运算而不需要太多交互的任务。


ParallelGC(jdk8环境默认)
Parallel Scavenge(新生代)+ Serial Old(老年代)
CMS
基于标记清除算法(有碎片)== 年轻代:复制算法;老年代:标记-清除算法(碎片)==
过程(初始标记和重新标记会STW,)
- 初始标记(仅仅只是标记一下GCRoots能直接关联到的对象,速度很快)
- 并发标记阶段(就是进行GC RootsTracing的过程)
- 重新标记阶段(则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个
阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短) - 并发清除

默认空间占比

G1
G1 收集器有两大特点
- G1 可以并发完成大部分 GC 的工作,这期间不会“Stop-The-World”
- G1 使用非连续空间,这使 G1 能够有效地处理非常大的堆。此外,G1 可以同时收集年轻代和年老代。G1 并没有将 Java 堆分成三个空间(Eden、Survivor 和 Old),而是将堆分成许多(通常是几百个)非常小的区域。这些区域是固定大小的(默认情况下大约为 2MB)。每个区域都分配给一个空间。 G1 收集器的 Java 堆如下图所示:
- 可以做到可预测的停顿
- G1最显著于CMS的,在于它对空间做了整理,这样减少了空间的碎片化。
- 压缩内存(重新整理,无碎片)

图上的 U 表示“未分配”区域。G1 将堆拆分成小的区域,一个最大的好处是可以做局部区域的垃圾回收,而不需要每次都回收整个区域比如年轻代和年老代,这样回收的停顿时间会比较短。
收集过程是:
6. 将所有存活的对象将从收集的区域复制到未分配的区域,比如收集的区域是 Eden 空间,把 Eden 中的存活对象复制到未分配区域,这个未分配区域就成了 Survivor 空间。理想情况下,如果一个区域全是垃圾(意味着一个存活的对象都没有),则可以直接将该区域声明为“未分配”。
7. 为了优化收集时间,G1 总是优先选择垃圾最多的区域,从而最大限度地减少后续分配和释放堆空间所需的工作量。这也是 G1 收集器名字的由来——Garbage-First。
3. GC

- Young GC
新生代内存的垃圾收集事件称为Young GC(又称Minor GC),当JVM无法为新对象分配在新生代内存空间时总会触发 Young GC,比如Eden 区占满时。新对象分配频率越高, Young GC 的频率就越高
新对象始终在Eden创建
如果Young GC后S0或S1区不足以容纳:未达到晋升老年代条件的新生代存活对象,会导致这些存活对象直接进入老年代
大对象或大数组存入老年代
Young GC 每次都会引起全线停顿(Stop-The-World),暂停所有的应用线程,停顿时间相对老年代GC造成的停顿,几乎可以忽略不计 - Old GC 、Full GC、Mixed GC
Old GC,只清理老年代空间的GC事件,只有CMS的并发收集是这个模式(老年代标记清除)
Full GC,清理整个堆的GC事件,包括新生代、老年代、元空间等(老年代标记整理)
触发场景:1,当老年代无法再分配内存的时候;2、元空间不足的时候 3,空间分配担保,
Mixed GC,清理整个新生代以及部分老年代的GC,只有G1有这个模式
full gc会回收方法区:
因为和堆的垃圾回收效率相比,方法区的回收效率实在太低,但是此部分内存区域也是可以被回收的
方法区的垃圾回收主要有两种,分别是对废弃常量的回收和对无用类的回收。
方法区中的类需要同时满足以下三个条件才能被标记为无用的类:
1.Java堆中不存在该类的任何实例对象;
2.加载该类的类加载器已经被回收;
3.该类对应的java.lang.Class对象不在任何地方被引用,且无法在任何地方通过反射访问该类的方法。
对象优先在 Eden 区分配
大对象直接进入老年代
长期存活的对象进入老年代
动态对象年龄判定
为了能更好地适应不同程序的内存状况,虚拟机并不总是要求对象的年龄必须达到阈值才能进入老年代。如果在 Survivor 区中相同年龄的所有对象的空间总和大于 Survivor 区空间的一半,则年龄大于或等于该年龄的对象直接进入老年代。
表现:年龄从小到大进行累加,当加入某个年龄段后,累加和超过survivor区域*TargetSurvivorRatio的时候,就从这个年龄段往上的年龄的对象进行晋升。
空间分配担保
在发生 Minor GC 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的空间总和,如果这个条件成立,那么 Minor GC 可以确保是安全的。如果不成立则进行 Full GC。
GC 性能衡量指标
**吞吐量**:这里的吞吐量是指应用程序所花费的时间和系统总运行时间的比值。我们可以按照这个公式来计算 GC 的吞吐量:系统总运行时
间 =应用程序耗时 +GC 耗时。如果系统运行了 100 分钟,GC 耗时 1 分钟,则系统吞吐量为 99%。GC 的吞吐量一般不能低于 95%。
**停顿时间**:指垃圾收集器正在运行时,应用程序的暂停时间。对于串行回收器而言,停顿时间可能会比较长;而使用并发回收器,由于
垃圾收集器和应用程序交替运行,程序的停顿时间就会变短,但其效率很可能不如独占垃圾收集器,系统的吞吐量也很可能会降低。
**垃圾回收频率**:多久发生一次指垃圾回收呢?通常垃圾回收的频率越低越好,增大堆内存空间可以有效降低垃圾回收发生的频率,但同时
也意味着堆积的回收对象越多,最终也会增加回收时的停顿时间。所以我们只要适当地增大堆内存空间,保证正常的垃圾回收频率即可。
垃圾收集器的种类很多,我们可以将其分成两种类型,一种是响应速度快,一种是吞吐量高。通常情况下,
CMS 和 G1 回收器的响应速度快,
Parallel Scavenge 回收器的吞吐量高。
在 JDK1.8 环境下,默认使用的是 Parallel Scavenge(年轻代)+Serial Old(老年代)垃圾收集器,
JVM调优 分析:(MAT : Memory Analyzer tool)


gc roots
常说的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots且没有被GC roots引用的对象。
1.虚拟机栈(说明正在运行)(栈帧中的本地变量表)中引用的对象;
2.方法区中的类静态属性引用的对象;
3.方法区中常量引用的对象;
4.本地方法栈中JNI(Java Native Interface,即一般说的Native方法)中引用的对象
两次标记
两次标记过程:
对象被回收之前,该对象的finalize()方法会被调用;两次标记,即第一次标记不在“关系网”(gc roots)中的对象。第二次的话就要先判断该对象有没有实现finalize()方法了,如果没有实现就直接判断该对象可回收;如果实现了就会先放在一个队列中,并由虚拟机建立的一个低优先级的线程去执行它,随后就会进行第二次的小规模标记,在这次被标记的对象就会真正的被回收了。
4. java运行时数据区域
定义
String str1=“hello”;
String str2=new String("hello");
String str3=new String("hello");
程序计数器
程序计数器占用较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器
方法区
方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
Java虚拟机栈的特征
后进先出(LIFO)栈
存储栈帧,支持Java方法的调用、执行和退出Java本地方法栈的特征
和虚拟机栈类似,主要是支撑Native方法的调用、执行和退出
有一些虚拟机(如HotSpot)将Java虚拟机栈和本地方法栈合并实现栈帧
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素。栈帧存储了方法的局部变量表,操作数栈(例如算术运算),动态连接和方法返回地址等信息。第一个方法从调用开始到执行完成,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
本文详细解析了Java中的各种垃圾回收算法,包括Serial、ParallelScavenge、ParallelGC、CMS和G1收集器的特点及应用场景。介绍了YoungGC、OldGC、FullGC和MixedGC的触发机制,以及GC性能衡量指标如吞吐量、停顿时间和回收频率。





被折叠的 条评论
为什么被折叠?



