对于jvm的内存分配和GC我想从以下顺序来说:
- 如何判断一个对象是否是存活的。
- 内存中常用的垃圾回收算法
- 常见的垃圾收集器
- jvm的内存分配和回收策略
如何判断一个对象是否是存活的
引用计数法:
每当有一个地方引用一个对象的时候计数器就会加1,当引用失效时就会减1。这种看起来是很好的,实现简单,效率也很高。但主流的jvm基本上没有选用这种方式来判断一个对象是否存活的。其主要原因就是它很难解决相互的循环引用。
可达性分析法:
通过“GC Roots”的对象作为起始点,从这些节点开始向下搜索。如果一个对象没有被搜索到(就是说不在这个以Roots根节点的树上),那么则说明对象可以被回收。
其中可以作为GC ROOTS的对象有以下几种情况:
- 栈中的本地变量表(局部变量)中引用的对象
- 方法区中的类的静态属性引用的对象
- 方法区中的常量引用的对象
注:
在这对象被回收时涉及到一个方法,Object对象里的finalize方法。在对象要被回收的时候,会先判断是否当前对象是否重写了finalize()方法,如果重写了则先把这个对象放在一个队列之中。由一个低优先级的Finalizer线程去执行。当在执行finalize()这个方法的时候,如果当前对象重新被已用了,也就是说拯救了自己那么就不会再回收了这个对象。
这个方法不建议大家使用,因为它的不确定性很大,无法保证调用顺序。有些人把这个方法作为“关闭外部资源”的方法,这种用法是错误的,我们完全可以用finally来代替。所以建议大家可以忘掉这个方法的存在。
方法区的回收:
上面说的都是回收在堆中进行的,那方法区中的常量和类的信息会不会被回收呢?答案是可以的。
但对于方法区的回收的效率是比较低的,相比于在年轻带的垃圾回收。
- 判定一个常量是否应该被回收是很简单的。和堆中的类似,判断有没有引用可以到达。
- 判断一个类是否是需要被回收的就比较复杂了:
首先这个类的所有实例应该是已经都被回收了的,它的类的ClassLoader已经被回收了,该类的Class对象也没有被引用,无法通过任何地方被调用(包括反射)。
对于一个类的回收是可以通过jvm参数来控制的,具体的参数可以去网上搜索。
在大量使用反射、动态代理、CGlib等框架和OSG这种频繁定义ClassLoader的场景都需要虚拟机应该具有类的卸载功能,以保证永久代中不会溢出。