JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

Java高频面试问题(二):Java中的弱引用、软引用是什么?

wys521 2024-11-14 19:28:20 精选教程 27 ℃ 0 评论

弱引用

弱引用与其他引用类型(如强引用、软引用)的主要区别在于,弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

弱引用非常适合用来解决内存泄漏或缓存等场景下的问题。例如,在缓存场景中,如果某个对象长时间没有被访问,那么可以将其放入缓存中,并使用弱引用来引用它。这样,当内存不足时,垃圾收集器会自动回收这些对象,从而释放内存。

在Java中,可以使用java.lang.ref.WeakReference类来创建弱引用。当目标对象不再被强引用引用时,垃圾收集器会自动回收目标对象,并将WeakReference对象添加到引用队列中,以便我们可以在需要时获取对目标对象的引用。

需要注意的是,由于弱引用随时可能被回收,因此在使用弱引用时需要检查是否为null,以避免空指针异常。同时,弱引用的回收行为是不确定的,因此不能将弱引用用于关键性的对象或操作

import java.lang.ref.WeakReference;  
  
public class WeakReferenceExample {  
  
    // 假设我们有一个大型对象,我们不希望它在缓存中永远存在  
    static class LargeObject {  
        private byte[] data = new byte[1024 * 1024 * 5]; // 5MB的数据  
  
        // 构造函数初始化数据  
        public LargeObject() {  
            // 初始化数据,这里只是填充了一些随机值  
            for (int i = 0; i < data.length; i++) {  
                data[i] = (byte) (i % 256);  
            }  
        }  
  
        // 释放资源的方法(在实际情况中可能更复杂)  
        public void cleanup() {  
            data = null;  
            System.gc(); // 提示进行垃圾回收,但并不能保证立即执行  
        }  
    }  
  
    public static void main(String[] args) {  
        // 创建一个大型对象的弱引用  
        WeakReference<LargeObject> weakRef = new WeakReference<>(new LargeObject());  
  
        // 获取对象的强引用  
        LargeObject obj = weakRef.get();  
        if (obj != null) {  
            System.out.println("Object is still alive: " + obj.hashCode());  
        } else {  
            System.out.println("Object has been collected");  
        }  
  
        // 清除强引用,使得只有弱引用指向该对象  
        obj = null;  
  
        // 尝试再次通过弱引用获取对象  
        obj = weakRef.get();  
        if (obj != null) {  
            System.out.println("Object is still alive after clearing strong reference: " + obj.hashCode());  
        } else {  
            System.out.println("Object might have been collected after clearing strong reference");  
        }  
  
        // 在某些情况下,垃圾收集器可能不会立即回收弱引用的对象。  
        // 可以通过执行一些操作来增加系统的内存压力,从而触发垃圾收集。  
        // 例如,创建一个大型数组来占用更多内存。  
        byte[] memoryLoad = new byte[1024 * 1024 * 20]; // 20MB的数据  
  
        // 再次尝试获取弱引用对象  
        obj = weakRef.get();  
        if (obj != null) {  
            System.out.println("Object is still alive after memory load: " + obj.hashCode());  
        } else {  
            System.out.println("Object has been collected after memory load");  
        }  
  
        // 清理资源(在实际情况中可能需要更复杂的逻辑)  
        if (weakRef.get() != null) {  
            LargeObject largeObject = weakRef.get();  
            largeObject.cleanup();  
            weakRef.clear(); // 清除弱引用  
        }  
    }  
}

在这个示例中,LargeObject类代表一个占用大量内存的对象。我们在main方法中创建了这个对象的一个弱引用weakRef。然后,我们通过调用get方法来检查对象是否仍然存活。当我们清除对象的强引用后,再次调用get方法可能会发现对象已经被垃圾收集器回收了。

请注意,垃圾收集器的行为是不确定的,它可能不会在get方法被调用时立即回收对象。为了模拟内存压力,我们创建了一个大数组memoryLoad来占用更多的内存。这可能会触发垃圾收集器运行,从而回收弱引用的对象。

最后,如果对象仍然存在(即弱引用weakRef.get()不为null),我们调用cleanup方法来释放LargeObject占用的资源,并通过weakRef.clear()来清除弱引用本身。这样做是为了确保即使在对象被回收之前,也能够进行必要的清理工作。

软引用

软引用是通过java.lang.ref.SoftReference类来实现的。一个SoftReference对象包含一个对实际对象的软引用。当系统内存足够时,垃圾收集器不会回收这些对象,只有在内存不足时,才会回收这些对象。此外,软引用可以与ReferenceQueue联合使用,以跟踪对象被回收的活动。

需要注意的是,由于软引用在内存不足时才会被回收,因此不能用来保证对象的永久存在。同时,由于软引用的回收行为是不确定的,因此在使用软引用时需要谨慎处理。

import java.lang.ref.SoftReference;  
import java.lang.ref.ReferenceQueue;  
import java.util.HashMap;  
import java.util.Map;  
  
public class SoftReferenceCache<K, V> {  
    private final Map<K, SoftReference<V>> cache;  
    private final ReferenceQueue<V> queue;  
  
    public SoftReferenceCache() {  
        this.cache = new HashMap<>();  
        this.queue = new ReferenceQueue<>();  
    }  
  
    public void put(K key, V value) {  
        cache.put(key, new SoftReference<>(value, queue));  
    }  
  
    public V get(K key) {  
        SoftReference<V> softRef = cache.get(key);  
        if (softRef != null) {  
            V value = softRef.get();  
            if (value != null) {  
                // 对象仍然有效,返回对象  
                return value;  
            } else {  
                // 对象已经被回收,从缓存中移除  
                cache.remove(key);  
                return null;  
            }  
        }  
        return null;  
    }  
  
    // 可以定期调用此方法清理已经被回收的对象  
    public void cleanup() {  
        SoftReference<V> ref;  
        while ((ref = (SoftReference<V>) queue.poll()) != null) {  
            K key = findKey(ref); // 查找对应的键,这取决于你如何存储键和软引用的映射关系  
            if (key != null) {  
                cache.remove(key);  
            }  
        }  
    }  
  
    // 这个方法需要根据你的具体实现来查找键,因为ReferenceQueue只保存被回收的对象,不保存键  
    private K findKey(SoftReference<V> ref) {  
        // 实现查找键的逻辑  
        return null;  
    }  
  
    // 测试代码  
    public static void main(String[] args) {  
        SoftReferenceCache<String, String> cache = new SoftReferenceCache<>();  
        cache.put("key1", "value1");  
        cache.put("key2", "value2");  
  
        System.out.println(cache.get("key1")); // 输出: value1  
        System.out.println(cache.get("key2")); // 输出: value2  
  
        // 假设这里执行了一些操作,导致内存紧张,软引用可能被回收  
  
        cache.cleanup(); // 清理已经被回收的对象  
        System.out.println(cache.get("key1")); // 可能输出: null,如果key1对应的对象被回收了  
        System.out.println(cache.get("key2")); // 可能输出: null,如果key2对应的对象被回收了  
    }  
}

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表