前言
使用Java开发,我们不需要去管理对象的生命周期,因为JVM会帮我们回收垃圾,不过这就是安全的吗,显然不是,因为JVM 的堆区存在了很多未回收的对象实例,那么就有可能发生内存溢出,所以我们就有必要在对强引用,弱引用,软引用,虚引用 有所了解。
强引用
我们可以这样理解强引用,我们家中有什么必需品?我觉得像床,衣柜这些东西是必需品,就算只有20平米的房间,我们依然需要, 那么强引用就可以看成是床,衣柜,我们在开发的时候,会创建很多对象实例,大部分都是强引用,强引用不会被JVM回收,即使 发生了OutOfMemory也不会回收,除非我们将其设置为null才会回收。
代码语言:javascript复制public class StrongReference {
public static void main(String[] args) {
User user = new User("steak");
System.out.println(user);
System.gc();
System.out.println(user);
}
}
代码语言:javascript复制User(username=steak)
User(username=steak)
上面创建的user就是一个强引用,我们使用System.gc()手动回收,user也没被回收,我们需要将user设值为null, JVM才会回收。
代码语言:javascript复制public class StrongReference {
public static void main(String[] args) {
User user = new User("steak");
System.out.println(user);
user = null;
System.gc();
System.out.println(user);
}
}
代码语言:javascript复制User(username=steak)
null
软引用
软引用可以理解为家里的非必需品,但是是建立在家里空间很足的情况下,如果家里空间很足,那么就不会丢掉,如果家里空间不足了, 就可以丢弃,就好比一些已经不常用的老桌子,家里空间足的时候,可以留着它,但是空间不足了,就可以把它丢了,在JVM中,如果内存空间 充足,那么就不会回收软引用,如果内存空间不足了,就会回收掉。
代码语言:javascript复制public class SoftReferenceTest {
public static void main(String[] args) {
SoftReference<Object> softReference = new SoftReference<>(new User("steak"));
System.out.println(softReference.get());
System.gc();
System.out.println(softReference.get());
}
}
代码语言:javascript复制User(username=steak)
User(username=steak)
如上因为空间是充足的,所以user没被回收。
软引用应用例子
比如我们需要读取大量的图片到内存中来,以提高查询的速度,如果每次每次读取都从文件服务器读取,那么性能会有很大的影响, 所以这里就存放在内存中,但是由于存放大量的图片在内存中,可能会导致内存溢出,所以使用软引用来解决这个问题,当内存不足的 时候,就会将其回收掉,内存充足时,就不会回收,这时可以直接从内存中回去,大大的提高了查询的速度。
代码语言:javascript复制public class ImagesCache {
private final Map<String, SoftReference<ImageInfo>> map = new HashMap<>();
public void add(ImageInfo imageInfo){
if (imageInfo != null){
SoftReference<ImageInfo> softReferenceImage = new SoftReference<ImageInfo>(imageInfo);
map.put(imageInfo.getUrl(),softReferenceImage);
}
}
public ImageInfo get(String url){
SoftReference<ImageInfo> img = map.get(url);
if (img == null){
System.out.println("image does not exist");
return null;
}
return img.get();
}
}
如上定义了一个Map来存放图片,key为图片url,value为图片对象的软引用。
弱引用
弱引用相对于软引用来说,它不会等到内存不足才被回收,而是只要JVM进行垃圾回收,都会对它进行回收。
代码语言:javascript复制public class WeakReferenceTest {
public static void main(String[] args) {
WeakReference<User> userWeakReference = new WeakReference<>(new User("steak"));
System.out.println(userWeakReference.get());
System.gc();
System.out.println(userWeakReference.get());
}
}
代码语言:javascript复制User(username=steak)
null
如上我们System.gc()进行手动回收,发现user引用被回收了。
注意:需要注意的是,如果有一个强引用和弱引用关联了,那么这个弱引用不会被回收。
代码语言:javascript复制public class WeakReferenceTest {
public static void main(String[] args) {
WeakReference<User> userWeakReference = new WeakReference<>(new User("steak"));
Object b = userWeakReference.get();
System.gc();
System.out.println(userWeakReference.get());
System.out.println(b);
}
}
代码语言:javascript复制User(username=steak)
User(username=steak)
User(username=steak)
如上将弱引用userWeakReference赋给了Object b,那么userWeakReference和b之间就关联了起来,所以弱引用就 无法被JVM回收。
我们将b设置为null后,就可以被回收
代码语言:javascript复制public class WeakReferenceTest {
public static void main(String[] args) {
WeakReference<User> userWeakReference = new WeakReference<>(new User("steak"));
Object b = userWeakReference.get();
System.out.println(userWeakReference.get());
b = null;
System.gc();
System.out.println(userWeakReference.get());
System.out.println(b);
}
}
代码语言:javascript复制User(username=steak)
null
null
虚引用
虚引用和其他的引用不一样,它随时都可能被回收,虚引用需要和引用队列ReferenceQueue一起来使用。
代码语言:javascript复制public class PhantomReferenceTest {
public static void main(String[] args) {
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<User> phantomReference = new PhantomReference<>(new User("123"),referenceQueue);
System.out.println(phantomReference.get());
}
}
代码语言:javascript复制null
null
从输出可以看出虚引用被回收了。
总结
从上面的强引用,软引用,弱引用,虚引用可以得出,强引用是我们使用得最多的,它是不会被回收的,即使发生了OOM,软引用会在内存不足的情况下被回收, 弱引用只要JVM进行垃圾回收,它都会被回收,虚引用则随时被回收。