1.强引用( StrongReference )并发
当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足的问题(不管什么时候都不会回收)jvm
public static void main(String[] args) { List list = new ArrayList<>(); int i = 1; while (i++ < Integer.MAX_VALUE) { list.add("ssssss"); } System.out.println("-------end-------"); }
2.软引用 (SoftReference)高并发
若是一个对象只具备软引用,则内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存(内存不足时候回收)spa
当内存足够的时候不会被回收,因此能正常输出对象的地址线程
public static void main(String[] args) { // 定义一个软引用 SoftReference<byte[]> soft = new SoftReference<byte[]>(new byte[1024 * 1024 * 1024]); // 消耗内存 for(int i = 0; i < 10;i++) { byte[] buff = new byte[1024 * 1024]; } // 建议执行GC System.gc(); // 打印结果 System.out.println(soft.get()); }
下面咱们把内存使用再 扩大1024倍, 当内存不够的时候,GC回收了内存,因此输出的内存地址就为null设计
public static void main(String[] args) { // 定义一个软引用 SoftReference<byte[]> soft = new SoftReference<byte[]>(new byte[1024 * 1024 * 1024]); // 消耗内存 for(int i = 0; i < 10;i++) { byte[] buff = new byte[1024 * 1024 * 1024]; } // 建议执行GC System.gc(); // 打印结果 System.out.println(soft.get()); }
3.弱引用(WeakReference)对象
只具备弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它的内存区域,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存(不管内存足不足都回收)blog
下面就是一个弱引用的例子,这个是软引用内存足够状况下的那个例子直接改为弱引用而来,证实:证实了即便是内存足够的状况下,只要执行了GC,就必定会回收弱引用生命周期
public static void main(String[] args) { // 定义一个弱引用 WeakReference<byte[]> weak = new WeakReference<byte[]>(new byte[1024 * 1024 * 1024]); // 消耗内存 for(int i = 0; i < 10;i++) { byte[] buff = new byte[1024 * 1024]; } // 建议执行GC System.gc(); // 打印结果 System.out.println(weak.get()); }
这里面也能够看出一个问题,就是执行到一半的时候,GC忽然回收了咱们的弱引用类型,致使没法正常完成,可是因为GC的线程优先级比通常线程的优先级都要低,保证在咱们使用完对象以后再回收,使咱们的系统更加稳定队列
4.虚引用(PhantomReference)
虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收,不过虚引用的做用不是为了咱们使用对象,而是为了记录。
为一个对象设置虚引用关联的惟一目的就是能在这个对象被收集器回收时收到一个系统通知
由于虚引用的对象咱们是使用不到的,它的get()始终返回一个null
public T get() {
return null;
}
建立虚引用的时候须要传入1个引用对象,和一个引用队列,当该虚引用对象被回收以后,不会立刻销毁,而会将这个虚引用加入引用队列
public PhantomReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); }
咱们再看看下面的例子
public static void main(String[] args) { // 定义一个引用队列 ReferenceQueue<byte[]> queue = new ReferenceQueue<byte[]>(); // 定义一个软引用类型 PhantomReference<byte[]> phantom = new PhantomReference<byte[]>(new byte[1024 * 1024],queue); // 第一次打印,这个时候还没执行内存回收,因此队列里面是null System.out.println(queue.poll()); // 消耗内存 for(int i = 0; i < 10;i++) { byte[] buff2 = new byte[1024 * 1024 * 1024]; } // 建议执行GC System.gc(); // 若是对象被回收,则会放进队列 System.out.println(queue.poll()); }
注意:以上全部例子的System.gc(); 并非执行GC的语句,而是建议jvm执行GC,并非执行了System.gc(); 就100%执行内存回收
高并发状况下会把咱们内存撑爆,由于咱们的对象引用由栈内存指向堆内存且咱们常常使用的是强引用,若是栈内存同时存在太多强引用的时候,栈内存没来得及释放,强引用过多一直存在致使GC没法回收内存,这个时候就会使咱们系统出现OOM。若是设计好线程数量使栈内不会有过多引用,合理释放掉栈内存引用,这个时候堆内存没有引用的状况下,则会回收掉,避免了高并发致使的OOM