GC所关心的问题就是:
(1)那些内存需要回收?
(2)何时回收?
(3)怎么回收?
关注点:在程序计数器、java虚拟机栈、本地方法栈中,这些内存都是随着线程的创建而创建,销毁而销毁,这部分是不需要太过于关注内存的回收问题;而在
java堆以及
方法区中,对象的分配和接口不同实现类,方法中的多个分支,这些需要多少内存是在运行期间才可以确定创建那些对象。这部分内存的分配和回收都是动态的,因此
垃圾收集器关注的都是这部分内存。
对象已死吗?
(1)引用计数法:
这是现在主流jvm没有在使用的方法;
这里可以作为了解,原理很简单,一个对象,每当一个地址指向它,引用+1,引用实效引用值则-1,当其引用值为0时,则表示该对象不再被引用。目前主流的JVM没有采用这种方法因为其无法解决
循环引用
的问题;
(2)可达性分析算法:
主流实现方法;
通过一系列的成为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当
一个对象到GC Roots没有任何引用链
相连时,证明此对象不可用。
主流的JVM判断对象是否存活即通过可达性算法识别;
java中的引用:
若reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。
这种定义显然是无法描述一些对象的:当内存足够可以保留,当内存不够了需要首先处理的对象们。
这就有了
强、软、弱、虚四种引用。四种引用强度依次减弱
(1)强引用:指普遍存在的,类似于
Object obj . = new Object(),这类引用称为强引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
(2)软引用:指向一些
还有用但是并非必须的对象。在系统将要发生内存溢出的时候,对这些对象列进回收范围之中进行第二次回收。若这次回收还没有足够的内存,才会抛出内存溢出异常。
SoftReference类来实现软引用。
(3)弱引用:也用来描述非必需对象,这类对象只能
生存到下次垃圾回收对象之前。当垃圾收集器工作时,弱引用的对象都会被回收。使用WeakReference实现。
(4)虚引用:最弱的引用关系,一个对象是否有虚引用
不会对其生存周期存在影响,也无法通过虚引用取得一个对象的实例。其存在的唯一目的就是能在这个
对象被收集器回收时收到一个系统通知。使用PhantomReference实现。
对象的死刑判定:
两次标记:(1)与GC Roots无引用链;(2)对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过一次了。
两次标记都符合要求,则表示这个对象需要被清除了。
方法区的回收:
(1)方法区试可以配置是否在方法区进行垃圾回收。(在hotspot中,可以认为方法区即是
永久代)
(2)永久代的垃圾回收主要回收两个部分:废弃的常量、无用的类。
(3)废弃常量回收:与java堆中的对象非常类似;
(4)无用类的回收:同时满足三个条件才可以称为时无用的类:1.该类的所有对象已经被回收。2.加载该类的ClassLoader已经被回收。3.该类对戏那个的java.lang.Class对戏那个美誉在任何地方被引用,无法在任何地方通过反射访问该类的方法。满足以上三个条件的虚拟机可以对其进行回收。ps:在大量使用反射、动态代理、CGLib等ByteCode框架,动态JS以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类写在的功能,以保证永久代不会溢出。
垃圾收集算法:
1.标记清除2.复制法3.标记-整理法
4.分代收集算法(结合了复制法和标记-整理法)
目前java堆中的新生代使用复制算法,老年代使用标记-整理法。
内存分配与回收策略:
(1)对象在新生代的Eden区分配,当Eden区没有足够的空间时,进行一次Minor GC。
ps
:新生代GC minor gc:指发生在新生代的垃圾回收动作,因为大多数对象都是朝生夕灭,因此minor gc发生的非常频繁;回收速度也很快
老年代gc full gc:发生在老年代的gc,一般速度比monor gc慢10倍以上。
如果发现统计数据说之前minor GC的平均晋升大小比目前old gen剩余的空间大,则不会触发minor GC而是转为触发full GC。默认的full gc触发以前会触发一次minor gc
(2)大对象(长字符串、数组)直接进入老年代。
(3)虚拟机给对象年龄的定义:对象在Eden出生,经过一次minor gc后若surviror区容纳的话,则进入surviror区,每熬过一次minor gc年龄加一岁。默认年龄增长到15(可配置)岁后就转入老年代。(surviror区所有同龄对象大小总和大于surviror区域的一半时,大于等于该年龄的对象直接进入老年代)