强引用
一般new的对象就是强引用,只有在没有引用时才会被垃圾回收掉。
Object a=new Object()
test:
public class M {
/*
* 此方法只有在被垃圾回收时调用
*/
@Override
protected void finalize() throws Throwable {
System.out.println("M has called finalize");
}
}
out:
public class ReferenceTst1 {
public static void main(String[] args) throws IOException {
M m=new M();
m=null; //为空时M对象才被回收
System.gc();
System.in.read();//阻塞JVM进程
}
}
public class StrengthReferenceTest {
public static void main(String[] args) {
M m=new M();
M n=m;
m=null;
System.gc();
//m为null,被回收
System.out.println(m);
//n指向M对象,不回收
System.out.println(n);
}
}
强引用对象回收:
public class Test1 {
static class B{
M m;
public B(M m){
this.m = m ;
};
}
public static void main(String[] args) {
//test1();
testB();
}
/**
* m,m1 指向同一内存地址(引用类型传递的是内存地址),此时M对象不会回收
*/
public static void test1(){
M m =new M();
M m1 =m;
m= null;
System.gc();
}
/**
* b对象回收,成员变量指向null, M对象无指向所以被回收
*/
public static void testB(){
B b =new B(new M());
b=null;
System.gc();
}
}
软引用:SoftReference
堆内存空间不足时,才会被回收
/*
* 设置jvm启动参数,堆内存起始内存和最大内存为20M
* -Xms20M -Xmx20M
*/
public class SoftReferenceTest {
public static void main(String[] args) {
//创建大小为10M的软引用字节数组
SoftReference<byte[]> bytes=new SoftReference<>(new byte[1024*1024*10]);
System.out.println(bytes.get());
//手动fullgc
System.gc();
//睡眠0.5s给回收时间
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//调用gc()后输出对象
System.out.println(bytes.get());
//重新申请堆内存15M的字节数组
byte[] newBytes=new byte[1024*1024*12];
//申请15M堆内存后输出对象
System.out.println(bytes.get());
}
}
如上代码,重新申请newBytes对象时,会自动回收bytes对象。软引用主要用作缓存。
弱引用:WeakReference
只要发生gc,就会跟随内存对象的回收而回收
public class WeakReferenceTest {
public static void main(String[] args) {
M m=new M();
WeakReference<M> weakReference=new WeakReference<M>(m);
m=null;
//弱引用weakReference指向的是M对象,不回收,输出对象
System.out.println(weakReference.get());
System.gc();
//m被回收,输出null
System.out.println(m);
//gc()后,弱引用weakReference对象被回收,输出null
System.out.println(weakReference.get());
}
}
弱引用的作用:
当两个或多个引用指向同一对象时(强引用),其中一个引用=null时,对象是不会回收的,因为存在其它强引用,若其他引用是弱引用,则会全部回收。
使用场景:ThreadLocal
如上T1和Key都指向ThreadLocal对象。若key是强引用,即使t1=null,但key还是指向ThreadLocal对象,所以会有内存泄漏,而使用弱引用的话,key会随t1的回收而被回收。
但还是有内存泄漏的情况,如果key被回收后为null,则导致整个value再也无法被访问到,因此依然存在内存泄漏。
解决方案:每次使用
public class ThreadLocalTest {
public static void main(String[] args) {
String str="hello world";
ThreadLocal<String> threadLocal=new ThreadLocal<>();
threadLocal.set(str);
System.out.println(threadLocal.get());
str=null;
threadLocal=null;
System.gc();
//此时ThreadLocalMap中key被回收为null,value还是指向 "hello world"内存空间。
threadLocal.remove();
//调用remove()后,才真正清空value.
}
}
不理解ThreadLocal内存泄漏,请再看下面这个例子:
public class RTest {
/* 经典用法----通过一个类去继承WeakReference,然后调用父类的构造方法,把某个对象设置成弱引用 */
static class R extends WeakReference<M> {
Object obj;
/**
* 前面一个是弱引用,后面一个是强引用
* @param referent
* @param obj
*/
public R(M referent, Object obj) {
super(referent);
this.obj = obj;
}
}
public static void main(String[] args) {
test();
// test()出栈,对象被回收,调用垃圾回收m1 对象此时被回收
System.gc();
}
public static void test(){
M m =new M();
M m1= new M();
//注意传参:m 是弱引用, m1 是强引用
R r =new R(m,m1);
m =null;
m1 =null;
//调用垃圾回收,m被回收,m1不会回收
System.gc();
System.out.println(r.get());
/*r= null;
System.gc();*/
}
}
虚引用:PhantomReference
Phantom:虚幻的
随时被垃圾回收器回收,并且虚引用访问不到被引用的对象。
/*
* 设置jvm启动参数,堆内存起始内存和最大内存为20M
* -Xms20M -Xmx20M
*/
public class PhantomReferenceTest {
//接受消息的队列
static final ReferenceQueue referenceQueue=new ReferenceQueue();
static final List LIST=new LinkedList();
public static void main(String[] args) throws IOException {
//虚引用对象
PhantomReference<M> phantomReference=new PhantomReference(new M(),referenceQueue);
new Thread(()->{
while (true) {
LIST.add(new byte[1024 * 1024]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(phantomReference.get()); //虚引用访问不了,输出null,但是对象并未回收
}
}).start();
new Thread(()->{
while (true){
Reference poll=referenceQueue.poll();
if (poll!=null){
System.out.println("...虚引用对象被JVM回收了:"+poll);
}
}
}).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
作用:管理堆外内存
NIO里有个DirectByteBuffer 也叫堆外内存。当监测到虚引用被垃圾回收器回收后,可以作出相应的处理去回收堆外内存。(netty)
堆外内存怎么回收?
sun.misc.Unsafe
Unsafe unsafe=Unsafe.getUnsafe();
allocateMemory(long var1) 手动申请内存
freeMemory(long var1) 手动释放内存