在java中对象的引用有强、软、弱、虚四种,这些引用级别的区别主要体现在对象的生命周期、回收时机的不同。
准备工作
1. 设置内存
为方便调试,将内存设置为16MB
依次点击菜单栏的Run—>Edit Configurations
点击 Modify options —> Add VM option
添加参数
我这里设置的内存是16m, 对应参数为 -Xms16m -Xmx16m
2. 内存检测
使用 runtime.freeMemory() 的api,用来获取到未使用内存大小
强引用
这是最常见的引用,就是平时用的"=" 赋值,当将变量指向null时则表示去除了强引用,当触发gc时变量会被回收。
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
runtime.gc();
System.out.println("创建数组前剩余内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
byte[] bytes = new byte[10 * 1024 * 1024];
System.out.println("创建数组后所用内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
System.gc();
System.out.println("触发gc后所用内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
// 去除引用关系
bytes = null;
System.gc();
System.out.println("去除引用关系后所用内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
}
打印结果:
创建数组前剩余内存:14.630088806152344MB
创建数组后所用内存:3.5891189575195312MB
触发gc后所用内存:3.6717300415039062MB
去除引用关系后所用内存:14.680328369140625MB
可以看出存在强引用关系时,即便触发gc该对象也不会被垃圾回收器回收
软引用
软引用引用的变量在gc时不会被回收,只有在内存不足时才会被回收。
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
runtime.gc();
System.out.println("创建数组前剩余内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
SoftReference<byte[]> softReference = new SoftReference<>(new byte[10 * 1024 * 1024]);
System.out.println("添加软引用后剩余内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
System.out.println("弱引用的引用对象:" + softReference.get());
System.gc();
System.out.println("触发gc后所用内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
System.out.println("弱引用的引用对象:" + softReference.get());
// 创建一个大于剩余内存的数组
byte[] newBytes = new byte[(int) (runtime.freeMemory() + 1024)];
System.out.println("弱引用的引用对象:" + softReference.get());
打印结果:
创建数组前剩余内存:14.631034851074219MB
添加软引用后剩余内存:3.5826187133789062MB
弱引用的引用对象:[B@5305068a
触发gc后所用内存:3.668914794921875MB
弱引用的引用对象:[B@5305068a
弱引用的引用对象:null
使用软引用时,触发gc对象不会被垃圾回收器回收,但当内存不足时,对象会被gc回收
弱引用
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
runtime.gc();
System.out.println("创建数组前剩余内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
WeakReference<byte[]> weakReference = new WeakReference<>(new byte[10 * 1024 * 1024]);
System.out.println("添加弱引用后剩余内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
System.out.println("弱引用的引用对象:" + weakReference.get());
System.gc();
System.out.println("弱引用的引用对象:" + weakReference.get());
}
打印结果:
创建数组前剩余内存:14.731277465820312MB
添加弱引用后剩余内存:3.5902481079101562MB
弱引用的引用对象:[B@5305068a
弱引用的引用对象:null
弱引用在gc触发后就会回收对象
虚引用
虚引用的创建与软引用、弱引用不同,它需要传入一个引用队列
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
runtime.gc();
System.out.println("创建数组前剩余内存:" + runtime.freeMemory() / 1024.0 / 1024 + "MB");
ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();
PhantomReference<byte[]> phantomReference = new PhantomReference<>(new byte[10 * 1024 * 1024], referenceQueue);
new Thread(() -> {
while (true) {
PhantomReference<byte[]> poll = (PhantomReference<byte[]>) referenceQueue.poll();
if (poll != null) {
System.out.println("虚引用对象被回收了");
break;
}
}
}).start();
System.out.println("gc前虚引用的引用对象:" + phantomReference.get());
System.gc();
System.out.println("gc后虚引用的引用对象:" + phantomReference.get());
}
输出:
创建数组前剩余内存:14.730300903320312MB
gc前虚引用的引用对象:null
虚引用对象被回收了
gc后虚引用的引用对象:null
不能通过虚引用去使用对象,主要用于跟踪对象被垃圾回收器回收的状态。