判断对象存活的算法
1.引用计数法 缺点:很难解决对象之间相互循环引用的问题。
2.根搜索算法(可达性分析算法)
以下几种可以判定为GC roots的对象
1.虚拟机栈中的引用的对象
2.方法区中的类静态属性引用的对象。
3.方法区中的常量引用对象。
4.本地方法栈中JNI(即一般说的Native方法)的引用的对象。
注意:根搜索算法中不可达的对象,不是非死不可,它现在处于缓刑阶段,至少要经历两次标记过程:第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法:
1.对象没有覆盖finalize()方法。
2.finalize()方法已经被虚拟机调用过。
这两种情况下都没有必要执行,对象死亡。
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为F-Queue的队列之中,Finalizer线程会去执行,执行但可能不会等待它(finalize()方法)运行结束(比如一个对象在finalize()方法中执行缓慢,或者发生了死循环甚至导致内存回收系统崩溃),finalize()方法是对象逃脱死亡名义的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己--只要重新与引用链上的任何一个对象建立关联即可。
强引用
软引用
弱引用 弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。
虚引用 虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。
虚引用主要用于检测对象是否已经从内存中删除。
回收方法区
永久代的垃圾回收主要回收两部分内容:1.废弃变量 2.无用的类
对于变量的回收机制与Java堆中的对象非常类似,如何断定一个类是否是无用的类呢
1.该类所有的实例都已经回收,也就是Java堆中不存在该类的任何实例
2.加载该类的classloader已经被回收
3.该类对应的java.lang.Class对象没有在任何地方引用,无法在任何地方通过反射访问该类的方法。
垃圾收集算法
标记清除算法 缺点 内存碎片化,可能存在大量不连续空间导致无法分配大对象。
标记-整理算法(标记-压缩算法)
复制算法
分代收集算法
类加载过程
1.加载
2.验证 连接的第一步,验证是为了确保Class文件的字节流中包含的信息符合虚拟机的要求。
3.准备 这个阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。
4.解析 虚拟机将常量池内的符号引用替换为直接引用的过程。
5.初始化 是类加载的最后一步,根据程序员通过程序制定的主观计划去初始化类变量和其他资源。
JVM加载
双亲委派模型
启动类加载器Bootstrap ClassLoader
扩展类加载器Extension ClassLoader
应用程序类加载器 Application ClassLoader
如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子类加载器才会尝试自己去加载。
破坏双亲委派模型
1.1.2之前,历史原因。
2.基础类如果要调用用户的代码,这时候双亲委派模型便会有问题了,这时需要破坏双亲委派模型。
为了解决这个困境,java引入了线程上下文类加载器,这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,那么将从父线程中继承一个;如果在应用程序的全局范围内都没设置,那这个类加载器默认就是应用程序类加载器。
3.用户对程序动态性的追求导致的。比如代码热替换、模块热部署。OSGI
OSGI环境下,类加载器不再是双亲委派模型中的树状结构,而且进一步发展为网状结构。