本文回答以下问题,文内可能有遗漏、错误或表达不够清晰的地方。
- 引用
① Java中的软引用、弱引用、虚引用的适用场景以及释放机制
② 软引用什么时候会被释放、弱引用什么时候会被释放
- 类加载
① 双亲委派机制的作用
② 如何自己实现一个classloader打破双亲委派
引用
① Java 中的软引用、弱引用、虚引用的适用场景以及释放机制
② 软引用什么时候会被释放、弱引用什么时候会被释放
Java 的四种引用类型:
● 强引用:我们 new 出来的对象就是强引用类型,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足的时候
● 软引用:使用 SoftReference 修饰的对象被称为软引用,软引用指向的对象在内存要溢出
的时候被回收(OOM之前回收)
● 弱引用:使用 WeakReference 修饰的对象被称为弱引用,若对象仅被弱引用指向,在任何发生GC的时候其均可被回收
● 虚引用:虚引用是最弱的引用,在 Java 中使用 PhantomReference 进行定义。无法通过虚引用获取与之关联的对象实例,当对象仅被虚引用引用时,在任何时候其均可被回收
。通常 PhantomReference 与引用队列 ReferenceQueue 结合使用,可以实现虚引用关联对象被垃圾回收时能够进行系统通知、资源清理等功能。
软引用、弱引用、虚引用的实现都继承了 Reference 这个类,一个 Reference 对象创建后的生命周期如下:
1、Native层,在GC时将需要被回收的 Reference 对象移动到 pending 链表;
2、Java层,源源不断的从 pending 链表中提取出元素,将 非 Cleaner 类型(继承自虚引用)
的对象加入到引用队列中,对于Cleaner 类型
的对象会调用其 clean 方法,该方法主要是用来做对应的资源回收。在堆外内存 DirectByteBuffer 中就是用 Cleaner 进行堆外内存的回收,这也是虚引用在 java 中的典型应用
。
软引用和弱引用应用场景:用于内存中不重要的数据缓存
类加载
① 双亲委派机制的作用
Java 里有如下几种类加载器:
● 引导类加载器(Bootstrap):负责加载支撑 JVM 运行的位于 JRE 的 lib 目录下的核心类库
,比如 rt.jar、charsets.jar 等
● 扩展类加载器(Ext):负责加载支撑 JVM 运行的位于 JRE 的 lib 目录下的 ext扩展目录
中的 JAR 类包
● 应用程序类加载器(App):负责加载 ClassPath系统类路径
下的类包,主要就是加载开发者自己写的那些类
● 自定义加载器:负责加载用户自定义路径下的类包
类加载器之间的关系:
● 启动类加载器(Bootstrap),由C++实现,没有父类
● 拓展类加载器(Ext),由Java语言实现,父类加载器为null
● 应用程序类加载器(App),由Java语言实现,父类加载器为拓展类加载器
● 自定义类加载器,父类加载器为应用程序类加载器
双亲委派机制:加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。
比如我们自己写的类A:
最先会找应用程序类加载器加载
,应用程序类加载器会先委托扩展类加载器加载
,扩展类加载器再委托引导类加载器
,
顶层引导类加载器
在自己的类加载路径里找了半天没找到A类,则向下退回加载A类的请求,
扩展类加载器
收到回复就自己加载,在自己的类加载路径里找了半天也没找到A类,又向下退回A类的加载请求给应用程序类加载器,
应用程序类加载器
于是在自己的类加载路径里找A类,结果找到了就自己加载了。
为什么要设计双亲委派机制?
● 沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心 API库 被随意篡改
● 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性
② 如何自己实现一个 classloader 打破双亲委派
- 重写类加载方法(loadClass)
Tomcat 容器打破了双亲委派机制,实现了web应用的隔离:Tomcat 给每个 Web 应用创建一个类加载器实例(WebAppClassLoader),该加载器重写了 loadClass 方法,优先加载当前应用目录下的类,如果当前找不到了,才一层一层往上找(不会一开始就向上传递)
网友评论