强软弱虚引用
强引用
当内存不足,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会对该对象进行回收。
强引用是我们最常见的普通对象的引用,只有还有强引用指向一个对象,就表明该对象还活着,垃圾回收器不会回收这种对象。在java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是强引用。
当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收器回收的,即使该对象以后永远都不会被用到也不会被JVM回收。因此强引用是造成java内存泄露的主要原因之一。
释放强引用:对于一个普通的对象,如果没有其他引用关系,只要超过了引用的作用域或者显式地将相应强引用赋值为null,一般就可以被垃圾回收了。
这里之所以将对象置为null是为了保证没有强引用将其引用。
public class StrongReferenceTest {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = o1;
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(o2);
}
}
null
java.lang.Object@49476842Process finished with exit code 0
软引用
软引用是一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftReference类实现。对于只有软引用的对象来说,当系统内存充足时,它不会被回收,当系统内存不足时,它可能被回收。
软引用通常用在内存敏感的程序中 ,比如高速缓存就有用到软引用,内存够用的时候就保留,不够就回收。
内存充足
@Test
public void memoryEnoughTest() {
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(softReference.get());
}
java.lang.Object@6ae40994
java.lang.Object@6ae40994
null
java.lang.Object@6ae40994Process finished with exit code 0
内存不足
/**
* JVM配置参数,设置内存小一点,new大对象导致OOM
* -Xms5m -Xmx5m -XX:+PrintGCDetails
*/
@Test
public void memoryNotEnoughTest() {
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc();
try {
byte[] bytes = new byte[30 * 1024 * 1024];
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(o1);
System.out.println(softReference.get());
}
}
[GC (Allocation Failure) [PSYoungGen: 1024K->504K(1536K)] 1024K->639K(5632K), 0.0015951 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1528K->504K(1536K)] 1663K->799K(5632K), 0.0008726 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 1528K->488K(1536K)] 1823K->966K(5632K), 0.0007872 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1512K->504K(1536K)] 1990K->1078K(5632K), 0.0005499 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1528K->504K(1536K)] 2102K->1262K(5632K), 0.0007168 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1528K->488K(1536K)] 2286K->1389K(5632K), 0.0007267 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1510K->320K(1536K)] 2411K->1549K(5632K), 0.0007209 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1344K->256K(1536K)] 2573K->1589K(5632K), 0.0086172 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] java.lang.Object@6ae40994
java.lang.Object@6ae40994
[GC (System.gc()) [PSYoungGen: 1279K->192K(1536K)] 2613K->1605K(5632K), 0.0006518 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 192K->0K(1536K)] [ParOldGen: 1413K->1227K(4096K)] 1605K->1227K(5632K), [Metaspace: 5266K->5266K(1056768K)], 0.0092927 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 1227K->1227K(5632K), 0.0004827 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 1227K->1227K(5632K), 0.0005959 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 1227K->1227K(4096K)] 1227K->1227K(5632K), [Metaspace: 5266K->5266K(1056768K)], 0.0041061 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 1227K->1227K(5632K), 0.0002905 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 1227K->1171K(4096K)] 1227K->1171K(5632K), [Metaspace: 5266K->5262K(1056768K)], 0.0091496 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
null
nulljava.lang.OutOfMemoryError: Java heap space at tupobi.top.demoall.reference.soft.SoftReferenceTest.memoryNotEnoughTest(SoftReferenceTest.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner 2. e v a l u a t e ( P a r e n t R u n n e r . j a v a : 268 ) a t o r g . j u n i t . r u n n e r s . P a r e n t R u n n e r . r u n ( P a r e n t R u n n e r . j a v a : 363 ) a t o r g . j u n i t . r u n n e r . J U n i t C o r e . r u n ( J U n i t C o r e . j a v a : 137 ) a t c o m . i n t e l l i j . j u n i t 4. J U n i t 4 I d e a T e s t R u n n e r . s t a r t R u n n e r W i t h A r g s ( J U n i t 4 I d e a T e s t R u n n e r . j a v a : 68 ) a t c o m . i n t e l l i j . r t . e x e c u t i o n . j u n i t . I d e a T e s t R u n n e r 2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner 2.evaluate(ParentRunner.java:268)atorg.junit.runners.ParentRunner.run(ParentRunner.java:363)atorg.junit.runner.JUnitCore.run(JUnitCore.java:137)atcom.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)atcom.intellij.rt.execution.junit.IdeaTestRunnerRepeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)Heap
PSYoungGen total 1536K, used 425K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
eden space 1024K, 41% used [0x00000000ffe00000,0x00000000ffe6a488,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 4096K, used 1171K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)
object space 4096K, 28% used [0x00000000ffa00000,0x00000000ffb24fe0,0x00000000ffe00000)
Metaspace used 5336K, capacity 5498K, committed 5632K, reserved 1056768K
class space used 619K, capacity 658K, committed 768K, reserved 1048576KProcess finished with exit code -1
适用场景
假如有一个应用需要读取大量的本地图片:
- 如果每次读取图片都要从硬盘读取则会影响性能
- 如果一次性能全部加载到内存中时间久了可能会OOM
此时可以用软引用解决这个问题。设计思路是:使用一个HashMap来保存图片路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的控件,从而既能有效地避免了OOM,也能进行图片缓存加载。
Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
弱引用
弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生命周期更短。
对于只有软引用的对象来说,只要垃圾回收机制在运行,不管JVM的内存空间是否充足,都会回收该对象占用的内存。
@Test
public void memoryEnoughTest() {
Object o1 = new Object();
WeakReference<Object> weakReference = new WeakReference<>(o1);
System.out.println(o1);
System.out.println(weakReference.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(weakReference.get());
}
java.lang.Object@6ae40994
java.lang.Object@6ae40994
null
nullProcess finished with exit code 0
WeakHashMap
@Test
public void hashMapTest() {
HashMap<Integer, String> map = new HashMap<>();
Integer key = new Integer(1);
String value = "HashMap";
map.put(key, value);
System.out.println(map);
key = null;
System.out.println(map);
System.gc();
System.out.println(map);
}
{1=hashmap}
{1=hashmap}
{1=hashmap}Process finished with exit code 0
@Test
public void weakHashMapTest() {
WeakHashMap<Integer, String> map = new WeakHashMap<>();
Integer key = new Integer(1);
String value = "WeakHasMap";
map.put(key, value);
System.out.println(map);
//将它置为null的原因是,保证只有弱引用指向该对象
key = null;
System.out.println(map);
System.gc();
System.out.println(map);
}
{1=hashmap}
{1=hashmap}
{}Process finished with exit code 0
虚引用
虚引用需要用java.lang.ref.PhantomReference来实现。
顾名思义,就是形同虚设,与其他几种引用不同,虚引用不会影响对象的生命周期。如果一个对象持有虚引用,那么它就和没有任何引用一样,原来还被谁引用用原来的规则。它不能单独使用也不能通过它访问对象 ,虚引用必须和引用队列(ReferenceQueue)联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。PhantomReference的get方法总是返回null,因此无法访问对应的引用对象。
设置虚引用关联度对象的唯一目的就是,在这个对象被垃圾回收后收到一个系统通知或者后续进行下一步操作。java允许使用finalize()方法在垃圾收集器将对象从内存中消除出去之前做必要的清理工作。
引用队列
@Test
public void test() {
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 这里设置弱引用与引用队列关联
WeakReference<Object> weakReference = new WeakReference<>(o1, referenceQueue);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
System.out.println("准备GC。。。。。。。。。");
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(weakReference.get());
// 当对象被回收时,该弱引用被放入引用队列中,referenceQueue.poll()就不为null了
System.out.println(referenceQueue.poll());
}
java.lang.Object@6ae40994
java.lang.Object@6ae40994
null
准备GC。。。。。。。。。
null
null
java.lang.ref.WeakReference@1a93a7caProcess finished with exit code 0
虚引用和引用队列
public class PhantomReferenceTest {
@Test
public void test() {
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o1, referenceQueue);
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
System.out.println("准备GC。。。。。。。。。");
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(phantomReference.get());
// 当对象被回收时,该虚引用被放入引用队列中,referenceQueue.poll()就不为null了
System.out.println(referenceQueue.poll());
}
}
java.lang.Object@6ae40994
null
null
准备GC。。。。。。。。。
null
null
java.lang.ref.PhantomReference@1a93a7caProcess finished with exit code 0