垃圾收集器与内存分配策略

目录

垃圾回收3问:

  1. 哪些内存需要回收?
    java堆和方法区,不可能被任何途径使用的对象需要被回收;具体算法可达性分析算法和引用计数算法;
  2. 什么时候回收?
    要看具体虚拟机的实现,一般内存使用到达一定的阀值或新对象无法分配内存的时候会触发垃圾回收;
  3. 如何回收?
    要看具体虚拟机的实现,一般有 标记-清除算法、复制算法、标记-整理算法、分代手机算法;

对象回收

判断对象是否该回收

引用计数算法—引用计数器,主流java虚拟机并没有使用这种算法

给对象添加一个计数器,当有引用的时候 +1,引用失效的时候 -1,当计数器为0的时候就是不可能再被使用了;但是她比较难判断对象之间的相互引用的问题;

class TestGC {
    public Object obj = null;
}
// 考虑这样一种情况
TestGC a = new TestGC();
TestGC b = new TestGC();
a.obj = b;
b.obj = a;
a = null;
b = null;
System.gc();
//请问a,b对象是否该回收呢?

可达性分析算法—GC Roots引用链,主流java虚拟机使用

通过 GC Roots 为起点,向下搜索,所走过的路径称为“引用链”,当一个对象到GC Roots 没有任何引用链,证明这个对象不可用;
GC Roots:
1. 虚拟机栈(栈帧中本地变量表)的引用对象;
2. 方法区中静态属性引用变量;
3. 方法区中常量引用对象;
4. 本地方法栈中中JNI(Native方法)的引用对象;
这里写图片描述

引用的分类—强、软、弱、虚

JDK1.2 以后引用分为了

强引用(Strong Reference)

普遍存在的引用Object a = new Object();

软引用(Soft Reference)

非必须但是还有用,在系统将要发生内存溢出时之前回收SoftReference类实现

弱引用(Weak Reference)

非必须对象比软引用弱一些,只能生存到下次垃圾回收之前。WeakReference类实现

虚引用(Phantom Reference)

最弱的引用关系无法通过虚引用获取一个对象实例,一个对象设置虚引用关联的目的就是能在这个对象被垃圾回收时收到一个系统通知PhantomReference类实现

对象最终被回收需要两次标记—算法一次、finalize()被调用后一次

对象最终被回收需要两次标记,可达性分析算法结束以后将被标记并被筛选,是否有必要执行finalize()方法,当对象没有覆盖finalize(),或者finalize()已经被虚拟机调用过了,就没必要执行了;如果有必要执行那么这个对象将被放到F-Queue队列中由虚拟机创建的低优先级Finalizer线程去执行他。虚拟机会调用这个方法,但不一定会等到这个方法执行结束,原因是如果该方法执行缓慢或者发生死循环会导致真个内存回收系统崩溃。然后GC会对F-Queue中的对象进行小范围标记,如果还是没有到达GC Roots的引用链那么将会被第二次标记,并放入即将回收集合中等待回收。finalize()只会被调用一次。且不一定会执行结束。不确定性大。不可依赖该方法

永久带的垃圾回收—废弃常量、无用的类

例如 String “ABC”,没有任何一个对象叫“ABC”也就是没有地方引用该常量,而且有必要的话才会回收。其他类、接口等与之类似。
类:1.该类所有实例都被回收;2.该类的加载器被回收;3.该类的Class对象没有地方被引用,无法通过反射访问该类的方法。
满足以上条件才可以被回收,但也不一定会被回收;
在大量使用动态代理反射的框架中例如CGLib;动态生成jsp以及OSGi这类频繁自定义CLassLoader的场景都需要虚拟机具备类卸载功能,以保证永久带不会溢出。

垃圾回收算法—标记-清除、复制、标记-整理、分代收集

标记-清除(Mark Sweep)

分为 标记 清除 两个步骤,首先标记需要回收的对象,标记完成后统一回收。标记清除会产生不连续的内存碎片。碎片过多会影像大对象的分配,导致触发新一次的GC。

复制(Copying)

内存分为两块,一块内存满了的时候就将还存活的对象一次性复制到另一边。HotSpot虚拟机将其分为3部分,Eden 80%,survivorFrom 10%,survivorTo 10%;对象分配在Eden,GC的时候将 Eden和survivorFrom还存活的对象一次性复制到survivorTo中。

标记-整理(Mark Compact)

和标记-清除算法差不多,但是后续步骤不是直接清理,而是将存活对象想一端移动。然后直接清除端边界以外的内存。

分代收集(Generational collection)

根据对象的存活周期不同将堆内存分为新生代老年代。根据各个年代的特点不同采取不同的收集算法。新生代每次都有大量对象死去,只有少量存活复制代价较小采用复制算法老年代存活几率较高采用标记-清除标记-整理算法;
新生代分为三个区域,一个Eden区和两个Survivor区,它们之间的比例为(8:1:1),这个比例也是可以修改的。通常情况下,对象主要分配在新生代的Eden区上,少数情况下也可能会直接分配在老年代中。Java虚拟机每次使用新生代中的Eden和其中一块Survivor(From),在经过一次Minor GC后,将Eden和Survivor中还存活的对象一次性地复制到另一块Survivor空间上(这里使用的复制算法进行GC),最后清理掉Eden和刚才用过的Survivor(From)空间。将此时在Survivor空间存活下来的对象的年龄设置为1,以后这些对象每在Survivor区熬过一次GC,它们的年龄就加1,当对象年龄达到某个年龄(默认值为15)时,就会把它们移到老年代中。
老年代中使用Major GC(Full GC),采用的标记-清除算法。

垃圾收集器

Serial收集器

单线程的垃圾收集器,采用标记-整理算法,简单高效但是GC时需要停止所有用户线程。

ParNew收集器

Serial的并发版本,采用标记-整理算法,作为新生代收集器,可与CMS结合

Parallel Scavenger收集器

采用复制算法,关注吞吐量 = 运行用户代码的时间/(用户时间 + 垃圾回收时间);

Serial Old 收集器

Serial的老年代版本 采用标记-整理算法,单线程

Parallel Old收集器

Parallel Scavenger的老年版,采用复制算法。

CMS (Concurrent Mark Sweep)收集器

采用标记-清除算法,追求并发收集低停顿。

G1 收集器

最新的垃圾收集器,将整个堆内存,划分为多个相同大小的区域(Region)。生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。
在G1中,还有一种特殊的区域,叫Humongous区域,它用来专门存放巨型对象。
Region之间的引用,新生代与老年代之间的引用,G1引进了RSet Remembered Set,当发生引用时如果是老年代引用了新生代的对象,并记录到Remembered Set中;将GC根节点的范围加入到Remembered Set可保证不对全堆扫描也不会有遗漏;

G1 Young GC

Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行。
收集前:
收集前
收集后:
收集后

Mix GC(混合收集)

混合式GC也是采用的复制的清理策略,当GC完成后,会重新释放空间。
收集前:
这里写图片描述
收集后:
这里写图片描述

链接

参考文章及本文图片来自:深入理解 Java G1 垃圾收集器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值