JVM内存回收机制简述

JVM内存回收机制涉及的知识点太多了,了解越多越迷糊,汗一个,这里仅简单作个笔记,主要参考《深刻理解Java虚拟机:JVM高级特性与最佳实践(第二版)》java

目前java的jdk默认虚拟机为HotSpot,所以本文涉及虚拟机相关内容都指HotSpot虚拟机算法

本文主要关注GC的回收:判断哪些对象可回收,如何回收,回收机制编程

判断哪些对象可回收

GC是经过对象是否存活来决定是否进行回收,判断对象是否存活主要有两种算法:引用计数算法、可达性分析算法多线程

  • 引用计数算法
    引用计数的算法原理是给对象添加一个引用计数器,每被引用一次计数器加1,引用失效时减1,当计数器0后表示对象不在被引用,能够被回收了,引用计数法简单高效,可是存在对象之间循环引用问题,可能致使没法被GC回收,须要花很大精力去解决循环引用问题
  • 可达性分析算法
    可达性分析的算法原理是从对象根引用(堆栈、方法表的静态引用和常量引用区、本地方法栈)开始遍历搜索全部可到达对象,造成一个引用链,遍历的同时标记出可达对象和不可达对象,不可达对象表示没有任何引用存在,能够被GC回收

如何回收

  找到可回收对象后,如何进行回收呢?并发

  内存回收算法主要有标记-清除、中止-复制、标记-整理,不一样算法使用不一样的场景,整体来讲中止-复制算法适合对象存活时间短,存活率低的新生代,标记-清除和标记-整理算法适合对象存活时间长,存活率高的老年代高并发

  • 标记-清除(Mark-Sweep)
    经过可达性分析算法标记全部不可达对象,而后清理不可达对象。这种算法会造成大量的内存碎片
  • 中止-复制(Stop-Copy)
    将新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区,回收时先将eden区存活对象复制到一个survivor0区,而后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另外一个survivor1区,而后清空eden和这个survivor0区,此时survivor0区是空的,而后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复,当survivor1区不足以存放 eden和survivor0的存活对象时,就将存活对象直接存放到老年代(这时咱们可能回想,如果老年代也满了咋办,如果老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收,如果内存还不够呢。。。,还不够那不废话了吗,OutOfMemory,不陌生吧哈哈)。从中止-复制算法的原理上咱们能够看到,这种算法对于存活率较低的对象回收有着很是高的效率,并且不会造成内存碎片,可是会浪费必定的内存空间,适合对象存活率较低的新生代使用,若是在对象存活率较高的老年代采用这种算法,那将会是一场灾难
  • 标记-整理(Mark-Compact)
    经过可达性分析算法标记全部不可达对象,而后将存活对象都向一个方向移动,而后清理掉边界外的内存。这种算法是将存活对象向着一个方向汇集,而后将剩余区域清空,这种算法适合对象存活率较高的老年代

GC回收机制:分代收集算法

JVM内存收集算法基本上都是采用分代收集算法,即将内存划分为新生代、老年代,也有人把方法区算作永久代spa

  • 新生代
    对象被建立时,内存分配都是发生在新生代(大对象直接分配在老年代),绝大多数对象都是朝生夕灭,建立后很快就会不在使用,变为不可达的对象,被GC回收掉。新生代的对象存活率很低(到底有多低?研究代表有高达98%的对象建立后很快就消亡,想象一下平时编程,除了全局变量,局部变量在退出调用方法后还有几个能存活),存活时间都很短,新生代发生的GC也叫作Minor GC,MinorGC发生频率比较高(不必定等Eden区满了才触发)
  • 老年代
    当对象在新生代发生了屡次Minor GC后仍然存活的对象即进入老年代,老年代的对象比新生多不少,固然了内存比新生代也大不少(大概比例是1:2,即新生代占用堆内存总量的1/3),当老年代内存满时触发Major GC即Full GC,Full GC发生频率比较低,老年代对象存活时间比较长,存活率标记高

通常来讲咱们所说的GC都是发生在新生代和老年代,新生代对象存活时间短,存活率低通常采用中止-复制算法,老年代对象存活时间长,存活率高,通常采用标记-整理、标记-清楚算法,具体采用何种算法和具体采用的垃圾收集器有关线程

GC里面有些相似未成年人和成年人,新建立的对象为新生代,新生代想要成为老年代须要通过必定的成长(总得一点点长大是吧),新建立的对象年龄为1,每发生一次Minor GC,存活对象的年龄增长1,当经历了15次Minor GC后,仍然存活的对象达到15岁,成达到法定成年年龄15岁(默认是15),正式成为成年人(老年代),对象成年后也就没有了年龄概念,直到对象死亡,会一直呆在老年代,固然也有一些老不死的(静态变量、常量等),会与世长存,除非地球灭亡(GC崩溃)对象

GC收集器

GC采用分代回收算好后,起着重要做用的是GC收集器,GC收集器分为新生代收集器和老年代收集器,不一样的收集器使用不一样的收集算法,有着不一样的特色,因为目前的收集器在内存回收时没法消除(Stop-the-world),即在回收内存时不可避免的中止用户线程,目前的收集器只能使停顿时间愈来愈短,可是没法完全消除,主要的收集其中Parallel Scavenge和Parallel Old是追求吞吐量为目标,其它的收集器都是追求高响应,低停顿,内存

  新生代收集器:Serial、PraNew、Parallel Scavenge

  老年代收集器:Serial Old、Parallel Old、CMS

  • Serial收集器(复制算法)
    新生代单线程收集器,标记和清理都是单线程,优势是简单高效。
  • Serial Old收集器(标记-整理算法)
    老年代单线程收集器,Serial收集器的老年代版本。
  • ParNew收集器(中止-复制算法)
    新生代收集器,能够认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。
  • Parallel Scavenge收集器(中止-复制算法)
    并行收集器,追求高吞吐量,高效利用CPU。吞吐量通常为99%, 吞吐量= 用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互相应要求不高的场景。
  • Parallel Old收集器(中止-复制算法)
    Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先
  • CMS(Concurrent Mark Sweep)收集器(标记-清理算法)
    高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu 追求高响应时间的选择
  • G1(Garbage-First)收集器(标记-整理算法、中止复制算法)GC最新型号,高富帅,高并发、可预测停顿、分代收集,不出意外将来主流将会逐步替代CMS,g1模糊了分代概念,虽然仍是分为新生代和老年代,可是新生代和老年代再也不是物理隔离,而是将内存分为n个region,以region为清理单位,总体采用标记-整理算法,region内部使用中止-复制算法。
相关文章
相关标签/搜索