强引用
就是GcRoots对象,是栈、方法区、本地里面引用指向对象,比如 A a=new A();
软引用
就是该引用的对象可有可无,只要不OOM是不会清除的,在内存溢出之前才会纳入gc范围。软引用一般用于一些计算结果和不需要实时获取的用户本身或者行为数据。
现在有redis之类的缓存了,一般情况下是用不到软引用去维护用户信息,比如几个业务需要看用户是否购买过第二单,你用软引用是不稳定的服务器重启就没了。但是有些应用场景是可以的,就比如A接口产生了一个“结果”,马上调用B接口而B接口不想重新计算那就可以用软引用,这个就没必要去用缓存因为B接口用完了这个“结果”就没有意义了/。对于这种“朝生夕死”的结果来说就可以用了,用缓存反而复杂了。
jvm参数
-Xms5m -Xmx5m
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
public class SoftReferenceHouse {
public static void main(String[] args) {
//List<SoftReference> houses = new ArrayList<>();//注释1
List<SoftReference> houses = new ArrayList<>();
int i = 0;
while (true) {
House h = new House();
SoftReference<House> buyer2 = new SoftReference<>(h);
houses.add(buyer2);
//houses.add(h);//注释2
System.out.println("i=" + (++i));
}
}
}
class House {
private static final Integer DOOR_NUMBER = 524288;
private int[] doors = new int[DOOR_NUMBER];
}
执行了就会看到i会变得很大之后还不会出现OOM,最后只会报出GC太频繁。把注释1和2替代软引用很快就会OOM
import java.lang.ref.SoftReference;
public class SoftReferenceWhenIdle {
public static void main(String[] args) {
House h = new House();
SoftReference<House> buyer2 = new SoftReference<House>(h);
h = null;
while (true) {
System.gc();
System.runFinalization();
if (buyer2.get() == null) {
System.out.println("house is null");
break;
} else {
System.out.println("still there");
}
}
}
}
执行会发现会打印很久still there因为软引用会挟持对象,除非内存不充足了。
弱引用
弱引用比前两者对对象的劫持能力更弱,如果对象只剩下弱引用这条路,那么在下一次YGC时就会被回收,但是YGC时间不确定,所以要注意WeakRefrence.get()会null。
jvm参数
-Xms5m -Xmx5m -XX:+PrintGCDetails
import java.lang.ref.WeakReference;
public class WeakReferenceWhenIdle {
public static void main(String[] args) {
House h = new House();
WeakReference<House> buyer3 = new WeakReference<>(h);
h = null;
long currentTimeMillis = System.currentTimeMillis();
int count = 0;
while (true) {
if (buyer3.get() == null) {
long duration = System.currentTimeMillis() - currentTimeMillis;
System.out.println("house is null and exited time=" + duration + "ms");
break;
} else {
System.out.println("still there. count=" + (count++));
}
}
}
static class House {
private static final Integer DOOR_NUMBER = 262144;
private int[] doors = new int[DOOR_NUMBER];
}
}
打印
still there. count=2870
[GC (Allocation Failure) [PSYoungGen: 1515K->320K(1536K)] 4200K->3344K(5632K), 0.0006251 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
still there. count=2871
house is null and exited time=30ms
Heap
PSYoungGen total 1536K, used 349K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
eden space 1024K, 2% used [0x00000000ffe00000,0x00000000ffe07728,0x00000000fff00000)
from space 512K, 62% used [0x00000000fff00000,0x00000000fff50000,0x00000000fff80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 4096K, used 3024K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)
object space 4096K, 73% used [0x00000000ffa00000,0x00000000ffcf40a8,0x00000000ffe00000)
Metaspace used 3397K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 362K, capacity 388K, committed 512K, reserved 1048576K
WeakHashMap比较常用到,key一般是"主体" value一般是”主题的附属“,这样就能实现主体被ygc后附属也自动清除。
import java.util.WeakHashMap;
public class WeakHashMapTest {
public static void main(String[] args) {
House seller1 = new House("1号卖家房源");
SellerInfo sellerInfo1 = new SellerInfo();
House seller2 = new House("2号卖家房源");
SellerInfo sellerInfo2 = new SellerInfo();
WeakHashMap<House, SellerInfo> weakHashMap = new WeakHashMap<>();
weakHashMap.put(seller1, sellerInfo1);
weakHashMap.put(seller2, sellerInfo2);
System.out.println("weakHashMap before null.size" + weakHashMap.size());
seller1 = null;
System.gc();
System.runFinalization();
System.out.println("weakHashMap after null.size" + weakHashMap.size());
System.out.println(weakHashMap);
}
static class House {
private String house;
House(String house) {
this.house = house;
}
}
static class SellerInfo {
private int info;
}
}
打印结果
weakHashMap before null.size2
weakHashMap after null.size1
{WeakHashMapTest$House@532760d8=WeakHashMapTest$SellerInfo@57fa26b7}
这个是怎么实现的呢?大概看下size的源代码
public int size() {
if (size == 0)
return 0;
expungeStaleEntries();
return size;
}
/**
* Expunges stale entries from the table.
*/
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
主要是queue,看下queue是啥时放入元素的,大概看了下就是在回收的时候。
虚引用
虚引用对对象没有任何挟持,甚至不能通过虚引用get对象,对一个对象设置虚引用的唯一目的就是在垃圾回收的时候收到一个系统通知。