一、垃圾回收
垃圾回收主要有三个关键词:which、when、how
which:哪些内存等待回收
when:什么时候进行回收
how:怎样回收
程序计数器和栈都是随线程而生,随线程而灭的。这部分占用内存的大小在编译时就可以确定了,因此这几个区域内存的分配和回收都具备确定性,不需要过多考虑。
而Java堆和方法区则不同,这部分占用内存的大小都要在运行时才能确定,因此内存的分配和回收都是动态的。因此垃圾回收主要考虑这部分
二、GC Roots
垃圾回收在对内存进行回收前,首先必须判断对象是否已死,即不再被任何途径使用,主要有两种方法来判断:
1)引用计数法:被引用一次则计数器加1,但是这种方法无法解决对象间相互引用的情况
2)根搜索算法:通过一系列的GC Roots对象作为起始节点,从它们开始向下搜索,搜索走过的路径称为引用链。当一个对象到GC Roots没有任何引用链,即不可到达时,则该对象是不可用的。若这些对象相互关联,则都视为可回收的
可作为GC Roots的对象有:
1)虚拟机(VM)栈中引用的对象
2)方法区中的类静态属性引用的对象
3)方法区中常量引用的对象
4)本地方法(JNI)栈中引用的对象
不可到达的对象不一定马上就会被回收,至少还需要经历两次标记过程:
1)经过根搜索发现没有跟GC Roots相关联的引用链
2)在finalize()方法执行后,但是在finalize()可以使一个对象复活(尽量不要使用)
三、Java引用
Java中的引用可以分为4类:
1)强引用:如用new创建的,这种引用只要存在,其引用的对象就不会被回收
2)软引用:SoftReference类实现,系统在发出内存溢出异常前,将其列入回收范围。若回收后内存仍不够,才会抛出内存溢出异常
3)弱引用:WeakReference类实现,只能生存到下一次GC前
4)虚引用:通过PhantomReference类实现,为一个对象设置虚引用的唯一目的就是希望能在这个被回收时收到一个系统通知
四、垃圾收集算法
垃圾收集算法主要分为三种:Mark_Sweep、Coping和Mark_Compact
1)标记-清除(Mark_sweep):先扫描所有对象,并对存活对象进行标记,然后再次扫描,将所有未标记的对象清除
这种方法不需要复制,但是效率较低,并且容易产生碎片
2)复制(Coping):将内存按照容量分为一块较大的Eden区和两块较小且相等的Survivor区(From和To),每次只使用Eden区和From区。回收时,将Eden区和From区中的存活对象一次性copy到To区中,然后清理掉Eden区和From区
这种方法虽然实现简单,且效率较高,但是可用内存却只为实际内存的一半,对内存影响较大
3)标记-整理(Mark_Compact):将所有存活的对象向内存的一端移动,然后将端边界之外的内存一次性全部清理
JVM中会根据对象的存活周期将内存分为几块,如新生代、老生代等。然后根据不同生代的特点,对内存采用不同的回收算法。一般对新生代采用Coping算法,对老生代采用Mark_Sweep或Mark_compact算法。
五、垃圾收集器
垃圾收集器主要有以下几种,其中有连线的表示可以搭配使用
一般JVM使用的是 Parallel Scavenge + Parallel Old 或 Par New + CMS 或 G1
六、内存分配策略
创建一个新对象时,一般是在Java堆中分配内存。而Java堆又可以分为新生代和老生代,一般大对象直接进入老生代,其它对象则为新生代。
一些概念:
Minor GC:对新生代对象的回收,发生的非常频繁,一般使用的是Coping算法(Eden + From —–> To)
Major GC:对老生代对象的回收
Full GC:对所有对象的回收,先进行Minor GC,然后再进行Major GC
年龄:JVM给每个对象定义了一个年龄计数器,每经历一次Minor GC,存活对象的Age就会加1.当Age到达一定值(默认为15)时,就会晋升为老年代。因此Age表示的是对象经历Minor GC的次数
晋升:在Minor GC时,若To区不足以容纳Eden区和From区的所有存活对象时,就会有对象需要从新生代搬运到老生代,这个过程就称为晋升
晋升的条件:
1)年龄:当Age到达一定程度时,就可以晋升为老年代
2)占用空间:当Survivor区中相同年龄所有对象的大小总和大于Survivor区空间的一半时,大于或等于该年龄的对象就晋升为老生代
对象的创建流程:
垃圾回收的基本过程为(一般指的是CMS收集器):
1)创建一个新生代对象时,首先会在Eden区进行内存分配,当Eden区空间不够时,则会触发一次Minor GC
2)使用Coping算法,将Eden区和From区中所有的存活对象Copy到To区,所有存活对象的Age加1
3)当To区不足以容纳Eden区和From区的所有存活对象时,就有对象从新生代晋升到老生代
4)当老生代空间不足时,就会触发Major GC对老生代进行回收,一般采用的是Mark_Sweep算法