java中引用类型
- 强引用 NormalReference(一个普通变量指向一个对象,引用消失以后,对象就会被GC)
Object o = new Object() - 软引用 SoftReference(有一个软引用对象,软引用对象中有个引用指向一个对象,这个对象是被软引用连着的,在GC的时候会被特殊处理,堆内存不够用的时候就会被回收)
/**
* -Xmx20M 设置最大堆内存为20MN
*/
public class SoftReferenceDemo {
public static void main(String[] args) {
SoftReference<byte[]> m = new SoftReference<byte[]>(new byte[1024*10245*10]);
System.out.println(m.get());
System.gc();
try {
Thread.sleep(500);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(m.get());
//再分配一个数组,堆内存装不下,这时候系统会进行GC
byte[] b = new byte[1024*1024*15];
System.out.println(m.get());
}
}
- 弱引用 WeakReference(有一个弱引用对象,弱引用对象中有个引用指向一个对象,这个对象只要遇到GC就会被回收)弱引用作用是解决内存泄露
public class WeakReferenceDemo {
public static void main(String[] args) {
WeakReference<Person> m=new WeakReference<Person>(new Person());
System.out.println(m.get());
System.gc();
System.out.println(m.get());
}
}
- 虚引用 PhantomReference(有跟没有差不多,永远get不到,作用是管理堆外内存)
ThreadLocal
threadLocal就是一个容器,A线程只能拿到自己放入threadLocal的东西,拿不到B线程放进去的东西
使用案例
不使用threadLocal
public class ThreadLocalDemo {
volatile static Person p = new Person();
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(p.name);
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
p.name="lisi";
}
}).start();
}
}
class Person{
String name="zhangsan";
}
上述打印结果lisi
使用threadLocal以后
public class ThreadLocalDemo {
// volatile static Person p = new Person();
static ThreadLocal<Person> t1= new ThreadLocal<Person>();
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t1.get());
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.set(new Person());
}
}).start();
}
}
class Person{
String name="zhangsan";
}
打印结果为null
源码解析
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
获取的是当前线程里面的threadlocalmap,自然B线程无法获取A线程放置的内容,threadLocal最明显的使用时spring中的事务@Transaction
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// new一个虚引用
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
// 这个Entry是一个weakReference
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
// 下面这句等效于 new WeakReference(new K()),即k对象是被弱引用指向的
super(k);
value = v;
}
}
流程:new 一个threadLocal,然后调用set方法,引用了当前线程的threadLocalMap,然后创建一个Entry对象即弱引用对象,让该弱引用对象指向new的threadLocal对象这个key,然后就将这个Entry放到threadLocalMap中
面试题1 为什么Entry要用弱引用
ThreadLocal<Person> t1=new ThreadLocal<>();
r1.set(new Person());
t1.remove();
假设Entry为强引用,因为是强引用,当我们写t1=null的时候(或者main方法退出),t1不再使用的时候,这个new出来的threadLocal应该被回收掉,可是因为在t1中set了一个new Person(),则ThreadLocalMap中仍然有个entry的key指向这个ThreadLocal对象t1,因此该对象无法回收,如果程序一直运行,则该对象永远无法回收,因为有个强引用永远指向他,造成了内存泄露问题
因此Entry为弱引用,ThreadLocalMap的key弱指向threadLocal对象t1,只要有GC,这个t1就会被回收
image.png当我们通过弱引用将ThreadLocal对象t1回收以后,就出现了key为null,但是value存在的情况,value则面临无法回收的局面,因为已经无法通过这个null找到这个value,导致越来越多的这种积累,造成内存泄漏
因此正常的threadLocal使用方法是确定new出来的Person不再引用以后,使用t1.remove()将整个entry行从ThreadLocalMap中删除
网友评论