本文已参与「新人创作礼」活动,一起开启掘金创作之路。
ThreadLocal
ThreadLocal可以放置线程级别的变量,使每个线程拥有自己的变量副本,修改不会影响其他线程。
-
ThreadLoacl 有一个静态内部类 ThreadLocalMap,其 Key 是ThreadLocal 对象,value是 Entry 对象,ThreadLocalMap对象是每个线程私有的threadlocal容器。
-
存在的问题
- 对于线程池,由于线程池会重用 Thread 对象,ThreadLocal 也会被重用,造成一系列问题。
- 内存泄漏,由于 ThreadLocal 是弱引用,但 Entry 的 value 是强引用,因此当 ThreadLocal 被垃圾回收后,value 依旧不会被释放,产生内存泄漏。(手动remove)
ThreadLocal 内存泄漏的原因:
从上图中可以看出,ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,Key(ThreadLocal)势必会被GC回收,这样就会导致静态的ThreadLocalMap中key为null, 而value还存在着强引用,只有thead线程退出或再次使用ThreadLocalMap时,value的强引用链条才会断掉。
为什么使用弱引用?
弱引用的ThreadLocal不会内存泄漏,因为即使没有手动删除,ThreadLocal也会被回收。当key为null,在下一次ThreadLocalMap调用set(),get(),remove() 方法的时候会清除value值。
总结:由于ThreadLocalMap是静态的,ThreadLocalMap与Thread的生命周期一样长,静态的ThreadLocalMap中key为null, 而value还存在着强引用,如果没有手动删除对应key,会导致内存泄漏。
动态代理
JDK动态代理
-
使用步骤
- 创建接口及实现类
- 实现代理处理器:实现 InvokationHandler接口 ,实现 invoke(Proxy proxy,Method method,Object[] args) 方法
- 通过 Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) 获得代理类
- 通过代理类调用方法。
Spring AOP的动态代理有两种方式,JDK 动态代理和 CGLIB(Code Generation Library)动态代理。
(1)JDK 动态代理:基于接口实现,代理类必须实现InvocationHandler 接口 并通过Proxy 类 的newProxyInstance方法生成动态代理对象,重写invoke方法调用被代理对象的方法。
(2)CGLIB动态代理:CGLIB是用于代码生成的类库,基于继承实现,运行时动态生成目标类的子类,要求目标类不能被final修饰。