在JVM垃圾标记的相关算法中的引用技术法和可达性分析算法都离不开"引用"这个概念。在JDK1.2之前,引用的概念对于一个对象来说只有被引用和不被引用两种,一定程度上会显得有些狭隘;这样对于一些"鸡肋"对象作用就显得有些无力。
如果我们希望描述这样一类对象:当内存空间还足够时,能保留在内存之中,如果内存空 间在进行垃圾收集后仍然非常紧张,那就可以抛弃这些对象—— 很多系统的缓存功能都符合这样的应用场景 。
JDK1.2版本之后Java对引用的概念进行了扩充:将引用分为了 强引用、软引用、弱引用和虚引用。其中引用的强度依次减弱:
强引用强引用(Strongly Reference) > > > 软引用(Soft Reference) > > > 弱引用(Weak Reference) > > > 虚引用(Phantom Reference)
强引用是我们最常用到的也是最熟悉的一种引用,通过new关键字创建出来的对象就是强引用的。
Object obj = new Object(); // 强引用对象
只要对象有强引用关联,那么 JVM 的垃圾收集就一定不会将这个对象收集。就算是内存空间不足也不会将此类对象回收,而是会直接抛OutOfMemoryError
。
但是我们可以手动的将强引用类型对象进行弱化,就是将引用对象的变量置为null
。
但是要注意一点,Java中集合类型的对象不能直接使用置为null
的方式,而是应该使用集合类中的clear()
方法进行释放。
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.clear();
}
- 强引用可以直接访问目标对象。
- 强引用所指向的对象在任何时候都不会被系统回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指向对象。
- 强引用是导致内存泄漏的主要原因。
软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存, 才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference
类来实现软引用。
SoftReference<Person> softReference = new SoftReference<Person>(p);
SoftReference
的构造器还有一个参数是ReferenceQueue
类型的,使用这个构造器,如果软引用所引用对象被垃圾回收,JVM就会把这个软引用加入到与之关联的引用队列中。
ReferenceQueue<Person> q = new ReferenceQueue<Person>();
SoftReference<Person> softReference = new SoftReference<Person>(p, q);
注意:
软引用类型的对象只在内存空间不足的情况下才进行回收,我们使用System.gc()
手动的进行回收只会起到通知作用,就算JVM扫描到它发现是软引用,也不会进行回收。
代码实验:
自定义一个Person类,重写finalize方法,观察是否进行回收。
public class ReferenceTest {
public final static int size = 1024 * 1024;
static class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
@Override
protected void finalize() throws Throwable {
System.out.println("对象: " + this.toString() + "将要被销毁");
}
}
public static void main(String[] args) {
Person p = new Person("Tom");
ReferenceQueue<Person> q = new ReferenceQueue<Person>();
SoftReference<Person> softReference = new SoftReference<Person>(p, q);
p = null; // 置为null
Person person = softReference.get();
System.out.println(person);
System.gc();
}
}
打印结果:
可以看到并没有进行回收。
[GC (System.gc()) [PSYoungGen: 1775K->488K(2560K)] 1775K->696K(9728K), 0.0007359 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 488K->0K(2560K)] [ParOldGen: 208K->639K(7168K)] 696K->639K(9728K), [Metaspace: 3317K->3317K(1056768K)], 0.0038992 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 2560K, used 56K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
eden space 2048K, 2% used [0x00000000ffd00000,0x00000000ffd0e3d0,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 7168K, used 639K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
object space 7168K, 8% used [0x00000000ff600000,0x00000000ff69fe60,0x00000000ffd00000)
Metaspace used 3326K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 363K, capacity 388K, committed 512K, reserved 1048576K
设置参数调整堆空间大小:
-Xms10m -Xmx10m -XX:+PrintGCDetails
并通过一些 *** 作使JVM内存不足:
public final static int SIZE = 1024 * 1024;
public static void main(String[] args) {
Person p = new Person("Tom");
SoftReference<Person> softReference = new SoftReference<Person>(p);
p = null;
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(new byte[SIZE]);
}
}
弱引用此时,堆空间发生GC *** 作,并且将弱引用对象进行了清理:
弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference
类来实现弱引用。
弱引用和软引用一样,在构造弱引用时,也可以指定一个引用队列,当弱引用对象被回收时,就会加入指定的引用队列,通过这个队列可以跟踪对象的回收情况。
软引用、弱引用都非常适合来保存那些可有可无的缓存数据。如果这么做,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用。
代码演示:
public class WeakReferenceTest {
static class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
@Override
protected void finalize() throws Throwable {
System.out.println("对象: " + this.toString() + "将要被销毁");
}
}
public static void main(String[] args) {
Person p = new Person("Tom");
p = null;
WeakReference<String> weakReference = new WeakReference(p);
System.gc();
}
}
输出结果:
对象: Person{name='Tom'}将要被销毁
Process finished with exit code 0
虚引用
虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。
在JDK 1.2版之后提供 了PhantomReference
类来实现虚引用。
public static void main(String[] args) {
String s = new String();
ReferenceQueue<String> q = new ReferenceQueue<String>();
PhantomReference phantomReference = new PhantomReference(s, q);
}
终结器引用
- 它用于实现对象的finalize() 方法,也可以称为终结器引用
- 无需手动编码,其内部配合引用队列使用
- 在GC时,终结器引用入队。由Finalizer线程通过终结器引用找到被引用对象调用它的finalize()方法,第二次GC时才回收被引用的对象
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)