垃圾回收的对象
我们申请的几乎所有对象都存储在堆上,并且堆空间在进程启动时就已经创建了一个初始化的空间,随着程序的运行,堆内存的使用率/大小增长情况下,为保证程序的内存空间足够使用,jvm会不定期的对不再使用的对象进行回收,这个过程就是GC。
关于内存结构可以参看上一节内容。
引用类型
- 强引用,强引用是对对象的强制引用,只要有强引用关联垃圾回收器就不会回收该对象。
- 软引用,SoftReference,系统会在即将发生内存溢出时回收该类引用,回收后若还是内存不足才会抛出内存溢出,该引用用在一些最好有,但也不是必须的对象引用上
- 弱引用,WeakReference,系统会在下一次GC时回收该引用对象,这里用在不阻碍回收的内存引用上,常用于一些会导致内存泄漏的对象引用上
- 虚引用,PhantomReference,幽灵引用,最弱(随时会被回收掉),垃圾回收的时候收到一个通知,就是为了监控垃圾回收器是否正常工作。
分配在栈上的对象引用不会参与GC
jvm为优化内存回收设计了逃逸对象分析。
逃逸算法是针对栈帧中创建的临时对象,若该对象会被当做参数传递到其他方法则满足逃逸的条件。
如果该对象还会被外部线程访问,则满足线程逃逸条件。
满足线程逃逸条件的对象会在堆内存中创建,而只满足全局逃逸条件的对象则可在栈中分配内存创建,这种对象将不会参与GC,生命周期跟随栈(即线程)
垃圾回收算法
-
垃圾收集
1.可达性分析
这个算法的基本思路是以RC Roots为起始点,找出所有被RC Roots引用的对象,之后回收那些没有被引用的对象。
RC Roots包括以下几种对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈JNI(native方法)中引用的对象
- jvm的内部引用(class对象,异常对象NullPointException、OutofMemoryError,系统类加载)
- 所有被同步锁(synchronized)持有的对象
- JVM 内部的JMXBean、JVMTI 中注册的回调、本地代码缓存等
- JVM 实现中的“临时性”对象,跨代引用的对象(在使用分代模型回收只回收部分代时)
2.引用计数
这个算法比较好理解,在对象中添加一个引用计数,当有对象引用该对象则计数加一,当引用失效则计数减一,不过目前主流的虚拟机都没有使用这个算法,原因是这个算法在处理相互引用的情况时需要额外的机制来协助计算,这样做的效率不是很高
-
垃圾回收
1.分代收集回收理论
分代收集理论是在以上两种收集算法基础上的一个优化算法,它将对象分为新生代和老年代,经过多次(不同的垃圾回收器不同有的是15有的是6)回收计算都没有被回收的对象叫做老年代,有一些垃圾回收器会将大对象直接放入老年代,有些回收时可选择
1、新生代回收(Minor GC/Young GC):指只是进行新生代的回收。
2、老年代回收(Major GC/Old GC):指只是进行老年代的回收。目前只有CMS 垃圾回收器会有这个单独的收集老年代的行为。(Major GC 定义是比较混
乱,有说指是老年代,有的说是做整个堆的收集,这个需要你根据别人的场景来定,没有固定的说法)
3、整堆收集(Full GC):收集整个Java 堆和方法区(注意包含方法区)
2.复制算法
将可用内存按照容量划分为大小相同的两块,每次只使用其中的一块,垃圾回收时将存活的对象复制到另一块上,然后再将这块内存整个清理掉,这样不仅没有内存碎片的问题,而且实现简单,运行高效;当然缺点也是很明显的,只能使用一半的内存空间
3.标记清除算法
这个算法比较好理解,垃圾收集时将可回收的对象做标记,垃圾回收时将标记的对象清理掉,它的缺陷是这种回收会产生大量的内存碎片,过多的内存碎片的存在将导致在创建大对象时由于找不到连续的内存空间将会导致另一次垃圾回收,且存在内存浪费
4.标记-整理算法
该算法是在标记清除算法上的一个进阶,垃圾收集时将需要回收的对象打上标记,然后将所有存活的对象向一端移动,最后清理掉末端边界以外的对象,这种算法虽然不会有内存碎片,但效率偏低。