什么是垃圾
简单来说已经不再被使用的资源就是垃圾
怎么判断一个资源是垃圾
- 引用计数法(一般不用)
- 对象被引用的时候引用计数+1,没有引用的时候引用计数-1,当引用计数为0时即可回收
- 循环引用会出问题
- 可达性分析
- 从GC Roots根节点出发,看该对象是否能被遍历到
java采用GC Roots可达性分析来判定是否可回收
- 跟踪收集器采用的为集中式的管理方式,全局记录对象之间的引用状态,执行时从一些列GC Roots的对象做为起点,从这些节点向下开始进行搜索所有的引用链,当一个对象到GC Roots 没有任何引用链时,则证明此对象是不可用的。
- 哪些对象可以作为 GC Roots 的对象:
- 虚拟机栈(栈帧中的局部变量区,也叫局部变量表)中引用的对象(Map map = new HashMap())
- 方法区中的类静态属性引用的对象( public static Map map = new HashMap())
- 方法去常量引用的对象
- 本地方法栈中 JNI (Native方法)引用的对象
3.GC Roots可达性图示
Object7,8虽然有被引用,但是没有GC Roots可以引用到,所以会被回收
java里四种引用状态(强软弱虚)对应着不同的垃圾回收机制
- 强引用
我们平常典型编码Object obj = new Object()中的obj就是强引用。通过关键字new创建的对象所关联的引用就是强引用。当这个引用还不为null时,就算内存满触发GC也宁愿报OOM(OutOfMemory)也不会去回收这个对象,当是null则会被回收。 - 软引用
软引用通过SoftReference类实现。内存满的时候会被回收 - 弱引用
弱引用通过WeakReference类实现。不管内存满不满,只要触发GC就会被回收 - 虚引用
虚引用也叫幻象引用,通过PhantomReference类来实现
四种基本GC 垃圾回收算法
-
Copying(复制算法):Young Generation(新生区)采用的minorGC就是用的这个是算法
简介:复制算法是将剩余存活的对象移到其他的空区域,适用于存活率不高的新生区
优点:不会产生碎片。
缺点:因为需要空区域来放复制的数据,浪费空间。
JVM里复制算法具体的实现:
新生区里有三个区域,分别为Eden,Survivor0,Survivor1,比例分别为8:1:1
一般来说新创建的对象会创建在Eden区里
垃圾回收器回收的区域是Eden区跟一个Survivor区(有可能是0也有可能是1),
第一次回收时会选中一个Survivor称为From区,另外一个Survivor区称为To区,不管怎样都会保证To区为空
然后会把Eden区跟From区存活的对象全部移到To区里,此时Eden区跟From区变为空。
第二次回收时,原本的From区跟To区相互调换,然后重复上面复制操作。
当To区已经满的时候,会把所有对象都放到Tenure Generation Space(老生区)
注,复制一次对象年龄会增加1,当年龄达到-XX:MaxTenuringThreshold(默认为15)的值会自动移到老生区。
Copying流程 - Mark-Sweep(标志清除算法):Tenure Generation Space(老生区)使用的是这个算法
简介
优点:不会浪费时间
缺点:耗时,且有碎片 - Mark-Compact(标记整理算法):在标记清除的算法基础上增加了整理的过程
优点:不会有碎片空间
缺点:花费时间长,效率低。 - Concurrent Mark-Sweep(并发标志清除算法):使用多线程来跑标志清除
优点:效率高
缺点:增加了不必要的花销
注:没有最合适的算法,都是各有利弊,现在一般是用的"分代收集算法",它其实不是一种算法,是一种算法思想,简单来说就是不同的生命周期用不同的收集算法,比如新生区用Copying,老生区用Mark-Sweep。
GC 算法是内存回收的方法论,垃圾收集器就是算法的落实的实现。
四种垃圾收集器
-
串行垃圾回收器(Serial)
它为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程,所以不适合服务环境。
image.png
-
并行垃圾回收器(Parallel)
多个垃圾收集线程并行工作,此时用户线程是暂停的,用于科学计算、大数据处理等弱交互场景。
image.png -
并发垃圾回收器(CMS)
用户线程和垃圾收集线程同时执行(不一定是并行,可能是交替执行),不需要停顿用户线程,互联网公司多用它,适用对相应时间有要求的场景。
image.png -
G1 垃圾回收器
G1 垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收。
Java垃圾回收器详解及配置说明
- Java 的 GC 回收的类型主要有:
UseSerialGC,UseParallelGC,UseConcMarkSweepGC,UseParNewGC,UseParallelOldGC,UseG1GC
Java 8 以后基本不使用 Serial Old
参数说明
DefNew : Default New Generation
Tenured : Old
ParNew : Parallel New Generation
PSYoungGen : Parallel Scavenge
ParOldGen : Parallel Old Generation - 查看当前配置的垃圾回收器
Java -XX:+PrintCommandLineFlags
-
垃圾收集器使用区域
- 新生代三种
串行 GC (Serial/ Serital Copying)
并行 GC (ParNew)
并行回收 GC (Parallel/ Parallel Scanvenge) - 老生代三种
串行 GC (Serial Old/ Serial MSC)
并行 GC (Parallel Old/ Parallel MSC)
并发标记清除 GC (CMS)
- 配置参数所对应的垃圾收集器
参数 | 新生代垃圾收集器 | 新生代算法 | 老年代垃圾收集器 | 老年代算法 |
---|---|---|---|---|
XX +Use SerialGc | SeriaIGC | 复制算法 | SerialOldGC | 标整 |
XX +Use ParNewGC | ParNew | 复制算法 | SerialOldGC | 标整 |
XX +Use ParalleIGC /-XX +Use ParallelOldGC | Parallel【Scavenge】 | 复制算法 | Parallel Old | 标整 |
XX:+Use ConcMark SweepGC | ParNew | 复制算法 | CMS+ Serial Old的收集器组合(Serial Old作为CMS出错的后备收集器) | 标清 |
G1里没有明确地区分新生代老生代,G1整体上采用标记整理算法,局部是通过复制算法,不会产生内存碎片:
-
XX +Use SerialGc 收集过程
SerialGc 收集过程
-
XX +Use ParNewGC 收集过程
ParNewGC 收集过程
-
XX +Use ParalleIGC /-XX +Use ParallelOldGC
ParalleIGC
-
XX:+Use ConcMark SweepGC(新生代采用ParNew并行处理,这边过程图就不展示了)
CMS老生代处理流程
- G1垃圾收集器
在G1里面,其实没有严格地把哪些区域划分为新生代或者老生代,所以他比较特殊,特地拿出来。
从JDK1.9开始,已经是默认使用G1来做垃圾收集器了。
- 以前收集器的特点
年轻代和老年代是各自独立且连续的内存块
年轻代收集器使用 eden + S0 + S1 进行复制算法
老年代收集必须扫描整个老年代区域
都是以尽可能的少而快速地执行 GC 为设计原则 -
G1是把内存区域分成一小块一小块的地址,每小块不固定为哪个生代,
主要改变是 Eden、Survivor 和 Tenured 等内存区域不再是连续的,而是变成了一个个大小一样的 region,每个 region 从 1M 到 32M 不等,一个 region 有可能属于 Eden、Survivor 或者 Tenured 内存区域。
总体上是采用标志整理算法,局部没小块是采用复制算法把存活的对象移到其他区域。
G1内存划分图示
G1的内存结构和传统的内存空间划分有比较的不同。G1将内存划分成了多个大小相等的Region(默认是512K),Region逻辑上连续,物理内存地址不连续。同时每个Region被标记成E、S、O、H,分别表示Eden、Survivor、Old、Humongous。其中E、S属于年轻代,O与H属于老年代。
H表示Humongous。从字面上就可以理解表示大的对象(下面简称H对象)。当分配的对象大于等于Region大小的一半的时候就会被认为是巨型对象。H对象默认分配在老年代,可以防止GC的时候大对象的内存拷贝。通过如果发现堆内存容不下H对象的时候,会触发一次GC操作 -
G1回收过程
image.png
网友评论