同步操作将从 flatfish/Java-Review 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
引用计数算法(Reference Counting)比较简单,堆每个对象保存一个完整的整型的 引用计算属性,用于记录对象被引用的情况
对于一个对象A,只要有一个对象引用类A,则A的引用计数器+1,当引用失效的时候,引用计数器就减去1,只要对象A的引用计数器为0;即表示A不可能再被使用,可进行回收
优点:
缺点:
循环引用
package cn.icanci.jvm;
/**
* @Author: icanci
*/
public class GCTest {
private byte[] bigSize = new byte[5 * 1024 * 1024];
Object reference = null;
public static void main(String[] args) {
GCTest obj1 = new GCTest();
GCTest obj2 = new GCTest();
obj1.reference = obj2;
obj2.reference = obj1;
obj2 = null;
obj1 = null;
System.gc();
}
}
小结
相当于引用计数算法而言,可达性分析算法不仅仅具有简单和执行高效等特点,更重要的是该算法可以有效的解决 引用计数算法中循环引用的问题,防止内存泄漏的发生
相对于引用计数算法,这里的可达性分析就是 Java、C#选择的,这种类型的垃圾收集通常也叫做 追踪性垃圾收集(Tracing Garbage Collection)
所谓 "GC Roots"根集合就是一组必须活跃的引用
基本思路
在Java语言中,GC Roots 包括以下几类元素
注意
Java语言提供了对象终止(finalization)机制来允许开发人员提供 对象被销毁之前的自定义处理逻辑
当垃圾回收器发现没有引用指向一个对象,即:垃圾回收此对象之前,总会先调用这个对象的finalize() 方法
finalize()方法允许在子类中被重新,用于在对象被回收时进行资源释放,通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等
永远不要主动调用某个对象的 finalize() 方法,应该交给垃圾回收机制调用,理由包括下面三点
从功能上来说,finalize()方法与C++中的析构函数类似
由于finalize()方法的存在,虚拟机中的对象一般处于三种可能的状态
如果从所有根节点都无法访问到某个对象,说明对象已经不再使用了,一般来说,此对象需要被回收,但是事实上,也并非是 “非死不可”的,这时候它们暂时处于”缓刑“的状态,一个无法触及的对象由可能再某个条件下”复活“自己 如果这样,那么对它的回收就是不合理的。为此虚拟机定义对象可能的三种状态
以上三种状态,只有在对象不可触及的时候才可以被回收
具体过程:判断一个对象objA是否可被回收,至少要经历2次标记过程
代码演示可复活的对象
package cn.icanci.jvm.string;
/**
* @Author: icanci
*/
public class CanReliveObj {
// 类变量,属于 GC Roots
public static CanReliveObj obj;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("调用当前类重写的finalize()方法");
// 当前对象与引用链上任意一个对象建立了联系
obj = this;
}
public static void main(String[] args) {
try {
obj = new CanReliveObj();
// 对象第一次调用自己
obj = null;
System.gc();
System.out.println("第一次GC");
Thread.sleep(2000);
if (obj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is alive");
}
System.out.println("第二次GC");
obj = null;
System.gc();
if (obj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is alive");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 第一次GC
* 调用当前类重写的finalize()方法
* obj is alive
* 第二次GC
*/
MAT 是Memory Analyzer的简称 它是一款功能强大的Java堆内存分析器,用于查找内存泄漏以及查看内存消耗情况
MAT是基于Eclipse开发的,是一款免费的性能分析工具
获取dump文件
实例代码
package cn.icanci.jvm.string;
import java.util.ArrayList;
import java.util.Date;
import java.util.Scanner;
/**
* @Author: icanci
*/
public class GcRootsTest {
public static void main(String[] args) {
ArrayList<Object> numList = new ArrayList<>();
Date birth = new Date();
for (int i = 0; i < 100; i++) {
numList.add(String.valueOf(i));
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("数据添加完毕,请操作");
new Scanner(System.in).nextLine();
numList = null;
birth = null;
System.out.println("numList birth 已经置空,请操作");
new Scanner(System.in).nextLine();
System.out.println("结束");
}
}
当成功区分处内存中存活对象和死亡对象之后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,以便有足够的可用内存空间为新对象分配内存
目前在JVM中比较常见的三种垃圾收集算法是 标记-清除算法(Mark-Sweep)、复制算法(Copying)、标记-压缩算法(Mark-Compact)
背景
执行过程
优点
缺点
特别的
应用场景
执行过程
标记-压缩算法的最终效果等同于 标记 - 清除算法执行完成之后,在进行一次内存碎片整理,因此,也可以称之为 标记-清除-压缩(Mark-Sweep-Compact)算法
二者的本质是标记清除算法是一种 非移动式的回收算法 标记-压缩是 移动式的 是否移动回收后的存活对象是一个优点缺点并存的风险决策
优点
缺点
没有一种算法可以完全取代其他算法,它们都有自己的优点和缺点
所以采用 不同生命周期的对象可以采取不同的收集方式,以便提供回收效率
核心思想:具体问题具体分析
几乎所有的GC都是采用分代收集(Generational Collecting)算法执行垃圾回收的
Hotspot虚拟机
以HotSpot中的CMS回收器为例,CMS是基于 Mark-Sweep实现的。对于对象的回收效率很高。对于碎片问题,CMS采用基于Mark-Compact算法的Serial Old回收器作为补偿措施:当内存回收热点(碎片导致的 Concurrent Mode Failure时),将采用Serial Old 执行Full GC对老年代进行内存的整理
增量收集算法
分区算法
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。