JVM学习笔记(二)
JVM垃圾回收与调优详解
JVM内存分配与回收
- 对象优先在Eden区分配。Eden区满执行MinorGC,存活对象拷贝到Survivor区。
- Survivor内存不足时,会直接将较大的对象分配至Order区。
- 长期存活的对象会被分配至Order区。对象每经过一次GC仍然存活就增加一岁,达到配置的最大年龄后就分配至Order区(-XX:MaxTenuringThreshold)。
对象存活算法
- 引用计数法 - 简单高效,但存在对象循环调用导致部分内存无法回收进而造成内存泄漏/溢出的问题。
- 可达性分析算法 - 通过(类加载器/Thread/虚拟机栈的本地变量表/static成员/常量引用/本地方法栈的变量)GCRoot节点向下查找,不存在引用的对象视为不可达对象。
- finalize()方法最终判定对象是否存活
3.1 第一次标记并进行一次筛选,判断是否有必要执行finalize()方法(为实现/已调用则视为没必要)。
3.2 第二次标记执行finalize()方法,会把当前对象放到F-Queue中,随后由虚拟机自建的低优先级Finalizer线程进行·回收,回收时重新建立引用链接则不回收,无引用链接则会被回收。
垃圾回收算法
- 标记清除算法:直接删除失去引用的对象,需要遍历整个内存空间,会导致内存不连续降低内存性能。
- 复制算法:内存分为两块,每次使用一块,这一块内存满了进行GC,并将存活的对象直接连续复制到另外一半空间,解决了产生内存碎片的问题,但是内存利用率降低,同时大对象复制慢。(suriver区)
- 标记整理算法:类似标记清除算法,区别在于它会移动存活对象覆盖整理后需要清除的空间,使得整理后内存连续。
- 分代收集算法:根据对象存活周期和特点将内存分为新生代(寿命短频繁整理空间小)和老年代(寿命长空间大),提高内存效率。
垃圾收集器
垃圾回收算法是GC的方法论,垃圾收集器是GC的实现。
Serial收集器
串行的单线程收集器,执行时会导致其他线程停止。新生代使用复制算法,老年代使用标记整理算法。
ParNew收集器
Serial收集器的多线程版本,其垃圾收集策略与Serial收集器一致。
Parallel Scavenge收集器
JDK8默认收集器(内存大于2g,CPU核数大于2),该收集器着重于提高吞吐量,提高CPU利用率,其他类似ParNew。
CMS收集器(Concurrent Mark Sweep)
主要用在老年代,使用标记清除算法,是一个真正意义的并发收集器。
- 初始标记:STW(STOP THE WORLD),并记录直接与root相连的对象,速度很快;
- 并发标记:同时开启用户与GC线程,根据出事标记进一步分析可达对象,但由于用户线程仍在运行,无法保证实时性,但会跟踪引用更新的情况。
- 重新标记:STW,修正并发标记期间变动的对象引用情况。
- 并发清除:开启用户线程,同时GC开始对未标记区域进行。
主要优点:并发手机、低停顿。缺点:CPU资源敏感(抢占服务资源),无法处理浮动垃圾(清除过程中产生的垃圾只能等到下次垃圾收集进行清理),使用标记清除算法会产生内存碎片
G1收集器(Garbage-First)
- 分而治之,将整个堆按照一定的大小分为多个Region。
- 分配大对象直接放入Humongous区,避免占用老年代空间减少老年代的整理频率,同时尽量避免大对象的复制。
- 可以通过参数指定在一个长度的时间内完成垃圾收集。收集器后台维护了一个优先列表,每次根据允许的收集时间,有限回收价值最大的Region。
TO BE CONTINUE
ZGC(JDK11)
TO BE CONTINUE