强引用(Strongly Re-ference):类似于new Object(),不回收强引用对象
软引用(Soft Reference):还有用,但非必须的对象。只被软引用关联着的对象。内存溢出异常前,会把这些对象进行第二次回收,如果这次回收还没有足够的内存, 才会抛出内存溢出异常。SoftReference类来实现
弱引用(Weak Reference):当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉被弱引用关联的对象。WeakReference类来实现弱引用
虚引用(Phantom Reference):无法通过虚引用来取得一个对象实例。为一个对象设置虚 引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知,PhantomReference类来实现虚引用。
Reference.get():返回结果为null,表示此对象已经被回收
Reference类首先把内存分为4种状态Active,Pending,Enqueued,Inactive:
一般来说内存一开始被分配的状态都是Active
Pending是指快要被放进队列的对象,也就是马上要回收的对象
Enqueued就是对象的内存已经被回收了,我们已经把这个对象放入到一个队列中,方便以后我们查询某个对象是否被回收
Inactive就是最终的状态,不能再变为其它状态
softReference很难模拟出内存异常前这种情况,而OOM异常又捕获不到
weakReference:
引用队列(ReferenceQueue):
GC过后,对象被回收,但是其引用softReference或者werkReference也是一个java对象,且这个对象其实也不再具有存在的价值了。所以需要一个清除机制。避免大量的softReference对象带来的内存泄露。此时可以用ReferenceQueue,作为softReference的参数。则当对象被回收时,对应的softReference对象会被列入ReferenceQueue队列里面(ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象),我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象
WeakHashMap弱键回收机制:
继承于AbstractMap,实现Map接口
和HashMap一样,WeakHashMap也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。当某个键不再正常使用时,会被从WeakHashMap中被自动移除。它对应的键值对也就从映射中有效地移除了
就是当key可以被回收时,GC后,整个key,value都自动移除
key必须是一个引用类型数据(引用类型才会在堆中),eg:
weakhashMap是一个散列表(https://blog.csdn.net/qq_35970057/article/details/108339306)
table为Entry数组(内部组成和HashMap,HashTable类似)
由上面的源码可以看出Entry是继承于WeakRefercnce
所以还是利用了Reference和ReferenceQueue
ThreadLocal:
为变量在每个线程中都创建一个副本,则每个线程可以访问自己内部的副本变量,解决多线程中数据因并发产生不一致问题
(eg:一个变量X,A线程改动了,B可能获取的是A线程改动后的X,所以ThreadLocal给A,B线程一个副本X1,X2)
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问
(1)每个Thread维护着一个ThreadLocalMap的引用
(2)ThreadLocalMap是ThreadLocal的静态内部类,Entry是ThreadLocalMap的静态内部类
(3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。
(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中
(5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。
(6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
ThreadLocal内存泄露问题
由流程图(threadLocal的set方法)可以看出,Entry是继承于WeakReference的,而ThreadLocal作为key,当key被回收的时候,value却没有被回收,且这块value永远不会被访问到,所以存在着内存泄漏
结论:
localThread使用完了,马上调用threadlocal的remove方法,则无事
线程对象及时被gc回收,这个内存泄露问题影响不大,但在threadLocal设为null到线程结束中间这段时间不会被回收的,就发生了我们认为的内存泄露
使用线程池的时候,线程结束是不会销毁的,会再次使用的。就发生了真正意义上的内存泄露
threadLocalMap中的get/getEntity方法中会对key判断Null,null的花会设置value也为null