类型 | 方式 | 优点 | 缺点 |
---|---|---|---|
自动垃圾回收 | 自动根据对象是否使用由虚拟机来回收对象 | 降低程序员实现难度、降低对象回收bug的可能性 | 程序员无法控制内存回收的及时性 |
手动垃圾回收 | 由程序员编程实现对象的删除 | 回收及时性高,由程序员把控回收的时机 | 编写不当容易出现悬空指针、重复释放、内存泄漏等问题 |
Class<?> clazz = loader.loadClass("com.itheima.my.A");
Object o = clazz.newInstance();
o=null;
URLClassLoader loader = new URLClassLoader(new URL[]{new URL("file:D:\\lib\\")});
loader=null;
Class<?> clazz = loader.loadClass("com.itheima.my.A");
clazz=null;
System.gc()
方法。System.gc()
方法并不一定会立即回收垃圾,仅仅是向Java虚拟机发送一个垃圾回收的请求,具体是否需要执行垃圾回收Java虚拟机会自行判断。缺点
线程Thread对象,引用线程栈帧中的方法参数、局部变量等
本地方法调用时使用的全局对象
Object obj = new Object(); // 强引用
SoftReference<Object> softRef = new SoftReference<>(new Object()); // 软引用
new SoftReference<对象类型>(对象)
。OutOfMemory
异常SoftReference
对象本身也需要被回收。如何知道哪些SoftReference
对象需要回收呢?SoftReference
的强引用删除public static void main(String[] args) {
Cache<Object, Object> build = Caffeine.newBuilder().softValues().build();
}
-Xmx200m
(设置堆的大小为200M) public static void main(String[] args) throws IOException {
byte[] bytes = new byte[1024 * 1024 * 100];
SoftReference<byte[]> softReference = new SoftReference<>(bytes);
bytes = null;
System.out.println(softReference.get());
byte[] bytes2 = new byte[1024 * 1024 * 100];
System.out.println(softReference.get());
}
// 运行结果:由于vm启动还要占用一部分的堆空间,如果byte释放,才可以放下byte2,
// byte释放后,softReference对象也会释放,所以返回为null
// [B@58ceff1
// null
-Xmx900m
是,count=8
,否则堆空间足够的情况下,不触发软引用对象的回收import java.io.IOException;
import java.lang.ref.SoftReference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
public class SoftReferenceExample {
public static void main(String[] args) throws IOException {
// 创建一个列表来存储软引用
ArrayList<SoftReference> softReferences = new ArrayList<>();
// 创建一个引用队列,用于跟踪被垃圾回收器回收的软引用对象
ReferenceQueue<byte[]> queues = new ReferenceQueue<>();
// 循环创建10个大对象,并为每个对象创建一个软引用
for (int i = 0; i < 10; i++) {
// 创建一个100MB的字节数组
byte[] bytes = new byte[1024 * 1024 * 100];
// 创建一个软引用,关联字节数组和引用队列
SoftReference<byte[]> studentRef = new SoftReference<>(bytes, queues);
// 将软引用添加到列表中
softReferences.add(studentRef);
}
// 创建一个软引用变量,用于从引用队列中取出被回收的软引用
SoftReference<byte[]> ref = null;
// 用于计数被垃圾回收器回收的软引用数量
int count = 0;
// 循环,从引用队列中取出软引用直到没有更多的软引用
while ((ref = (SoftReference<byte[]>) queues.poll()) != null) {
// 对回收的软引用计数
count++;
}
// 输出被回收的软引用数量
System.out.println(count);
}
}
ReferenceQueue
来跟踪垃圾回收器回收的对象。当软引用所引用的对象被回收时,软引用对象本身会被添加到ReferenceQueue
中。import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
/**
* 软引用案例 - 学生信息的缓存
*/
public class StudentCache {
private static final StudentCache cache = new StudentCache();
private final Map<Integer, StudentRef> StudentRefs;// 用于Cache内容的存储
private final ReferenceQueue<Student> q;// 垃圾Reference的队列
// key:当前学生id
// 构建一个缓存器实例(类的构造方法)
private StudentCache() {
StudentRefs = new HashMap<>();
q = new ReferenceQueue<>();
}
// 取得缓存器实例
public static StudentCache getInstance() {
return cache;
}
public static void main(String[] args) {
for (int i = 0; ; i++) {
StudentCache.getInstance().cacheStudent(new Student(i, String.valueOf(i)));
}
}
// 以软引用的方式对一个Student对象的实例进行引用并保存该引用
private void cacheStudent(Student em) {
cleanCache();// 清除垃圾引用
StudentRef ref = new StudentRef(em, q);
StudentRefs.put(em.getId(), ref);
System.out.println(StudentRefs.size());
}
// 清除那些软引用Student对象已经被回收的StudentRef对象
private void cleanCache() {
StudentRef ref;
// q中存放被回收数据的软引用对象
while ((ref = (StudentRef) q.poll()) != null) {
StudentRefs.remove(ref._key);
}
}
// 继承SoftReference,使得每一个实例都具有可识别的标识。
// 并且该标识与其在HashMap内的key相同
private class StudentRef extends SoftReference<Student> {
private Integer _key;
public StudentRef(Student em, ReferenceQueue<Student> q) {
super(em, q);
_key = em.getId();
}
}
}
class Student {
int id;
String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
WeakReference
类来实现弱引用,弱引用主要在ThreadLocal
中使用。WeakReference<Object> weakRef = new WeakReference<>(new Object()); // 弱引用
public static void main(String[] args) throws IOException {
byte[] bytes = new byte[1024 * 1024 * 100];
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes);
bytes = null;
System.out.println(weakReference.get()); //[B@58ceff1
System.gc(); //通知Gc回收对象
System.out.println(weakReference.get()); //null
}
PhantomReference
类来创建虚引用。例如:ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue); // 虚引用
终结器引用是一种特殊的引用类型,用于与对象的终结器(Finalizer)相关联。在Java中,每个对象都可以有一个终结器方法,该方法在对象被垃圾回收之前被调用。终结器引用可以用来保留具有终结器的对象,以便在垃圾回收器运行时调用其终结器方法。
终结器引用指的是在对象需要被回收时,终结器引用会关联对象并放置在Finalizer类中的引用队列中,在稍后由一条由FinalizerThread线程从队列中获取对象,然后执行对象的finalize方法,在对象第二次被回收时,该对象才真正的被回收。在这个过程中可以在finalize方法中再将自身对象使用强引用关联上,但是不建议这样做
/**
* 终结器引用案例 仅用于学习
*/
public class FinalizeReferenceDemo {
public static FinalizeReferenceDemo reference = null;
public void alive() {
System.out.println("当前对象还存活");
}
@Override
protected void finalize() throws Throwable {
try{
System.out.println("finalize()执行了...");
//设置强引用自救
reference = this;
}finally {
super.finalize();
}
}
public static void main(String[] args) throws Throwable {
reference = new FinalizeReferenceDemo();
test();
//Java虚拟机对任何给定对象调用finalize方法的次数永远不会超过一次
test();
}
private static void test() throws InterruptedException {
reference = null;
//回收对象
System.gc();
//执行finalize方法的优先级比较低,休眠500ms等待一下
Thread.sleep(500);
if (reference != null) {
reference.alive();
} else {
System.out.println("对象已被回收");
}
}
}
运行结果:只能救活一次,原因:Java虚拟机对任何给定对象调用finalize方法的次数永远不会超过一次
当前对象还存活
对象已被回收
注意:终结器引用已被认为是不安全和不推荐使用的,因为终结器的执行是不可预测的,并且可能导致性能问题和资源泄漏。
在Java 9之后,终结器引用已被废弃,推荐使用其他方式来进行资源释放和清理操作,如使用try-with-resources语句块或实现AutoCloseable接口。
在实际开发中,建议尽量避免使用终结器引用,并采用更可靠和可控的方式来处理对象的资源释放和清理操作。