jvm垃圾回收机制,jvm垃圾回收器有哪几种

  jvm垃圾回收机制,jvm垃圾回收器有哪几种

  垃圾:简单来说,内存中不再使用的内存空间就是垃圾。

  垃圾收集(GC):顾名思义,就是释放垃圾占用的空间,防止内存泄漏。有效使用可用内存,清除并回收内存堆中的死对象或长时间不使用的对象。

  Java出来之前,大家都在拼命写C或者C程序。这时候就产生了很大的矛盾。C等语言在创建对象的时候,要不断地开放空间,不使用的时候,要不断地释放空间。他们必须编写构造函数和析构函数,这些函数被反复分配,然后不断地被析构。所以有人提出,可以写一个程序来实现这个功能,每次创建和释放一个控件就重用这个代码,不需要重复编写。

  1960年,基于MIT的Lisp首先提出了垃圾收集的概念,用来处理C语言等连续的析构,而Java还没有诞生!所以其实GC并不是Java的专利,GC的历史远远大于Java!Java垃圾收集机制垃圾收集主要针对Java堆

  Java内存运行时区的程序计数器、虚拟机栈、本地方法栈都是和线程一起生一起死的;堆栈中的堆栈帧随着方法的进入和退出以有序的方式执行堆栈出和堆栈入操作。每个栈帧分配多少内存基本上在类结构确定的时候就知道了(虽然会在运行时被JIT编译器优化)。所以这些区域的内存分配和回收是确定性的,不需要过多考虑回收的问题,因为当方法或者线程结束时,内存自然会被回收。

  但是,Java堆不一样。一个接口中多个实现类所需的内存可能不同,一个方法中多个分支所需的内存也可能不同。我们只能知道程序运行时会创建哪些对象。这部分内存的分配和回收是动态的,垃圾收集器关注的是这部分内存。

  如何定义垃圾引用计数算法(可达性计数)在对象头中分配一个空间来保存对象的引用计数。如果该对象被其他对象引用,其引用计数将增加1。如果删除了对该对象的引用,其引用计数将减1。当对象的引用计数为0时,对象将被回收。

  优点:实现简单,效率高。缺点:不能解决对象之间的循环引用问题。

  可达性分析算法(根搜索算法)可达性分析算法的基本思想是通过一些叫做引用链(GC根)的对象从这些节点开始搜索,搜索的路径叫做(引用链)。当一个对象连接到GC根而没有任何引用链时(即从GC根节点到这个节点是不可达的),证明这个对象是不可用的。

  在Java语言中,可以用作GC根的对象包括以下四种:

  虚拟机堆栈中引用的对象(堆栈框架中的局部变量表)、对象方法区中类静态属性引用的对象、对象方法区中常量引用的对象、局部方法堆栈中JNI(一般为原生方法)引用的对象,如基本数据类型对应的类对象、一些常驻异常对象(NPE)、类加载器等。所有同步锁定的对象都反映了Java虚拟机内部情况的JMX bean、注册在JVMTI中的回调以及本地代码缓存。

  目前主流的Java虚拟机都使用精确GC,HotSpot虚拟机使用一套叫做OopMap的数据结构来达到精确GC的目的。

  在HotSpot中,一个对象的类型信息有自己的OopMap,它记录了该类型对象中什么样的数据在什么样的偏移量上。因此,从物体向外扫描可以是准确的;这些数据是在类加载期间计算的。

  每一个JIT编译的方法还会在一些特定的位置记录OopMap,当一个方法的指令被执行时,堆栈和寄存器中的哪些位置被引用。这样,当GC扫描堆栈时,就会查询这些OopMap,知道引用在哪里。这些具体位置主要在:

  1.循环的结束。2.方法就在调用方法的调用指令返回之前/之后。3.可能引发异常的位置。在OopMap的帮助下,JVM可以快速枚举GC根,但是JVM不会为每条指令生成一个OopMap。

  这些记录OopMap的特定位置被称为安全点,即当前线程只有在执行到安全点后才被允许挂起GC。

  如果对象引用关系在一段代码中没有改变,并且在这个区域的任何地方启动GC都是安全的,那么这个区域就叫做安全区域。分类强引用:强引用强引用是最常用的引用。如果一个对象有一个强引用,垃圾收集器将永远不会回收它。当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError,这样会使程序异常终止,也不会通过随意回收带有强引用的对象来解决内存不足的问题。

  public class Main { public static void Main(String[]args){ new Main()fun1();} public void fun 1(){ Object Object=new Object();Object[] objArr=新对象[1000];} }软引用:软引用软引用用于描述一些有用但不必要的对象。在Java中用java.lang.ref.SoftReference类表示。对于与软引用相关联的对象,JVM只会在内存不足时回收该对象。所以这个可以用来解决OOM的问题,这个特性非常适合缓存:比如网页缓存,图片缓存等。

  软引用可以与ReferenceQueue结合使用。如果软引用所引用的对象被JVM回收,那么软引用将被添加到与之相关联的引用队列中。以下是一个使用示例:

  导入Java . lang . ref . soft reference;public class Main { public static void Main(String[]args){ soft reference String Sr=new soft reference String(new String( hello ));system . out . println(Sr . get());}}WeakReference:弱引用弱引用和软引用的区别在于,只有弱引用的对象生命周期更短。垃圾收集器线程在扫描所辖内存区域的过程中,一旦发现弱引用的对象,无论当前内存空间是否足够,都会回收其内存。但是,由于垃圾收集器是一个低优先级的线程,它可能不会很快找到那些具有弱引用的对象。

  导入Java . lang . ref . weak reference;public class Main { public static void Main(String[]args){ weak reference String Sr=new weak reference String(new String( hello ));system . out . println(Sr . get());system . GC();//通知JVM的gc进行垃圾收集system . out . println(Sr . get());}}PhantomReference:虚拟引用“虚拟引用”也叫幽灵引用或幻影引用。顾名思义,只是名存实亡。与其他引用不同,虚拟引用不决定对象的生命周期。如果一个对象只持有一个虚拟引用,它可以在任何时候被垃圾收集器收集,就像它没有任何引用一样。虚拟引用不同于以前的软引用和弱引用,它不影响对象的生命周期。在Java中,用java.lang.ref.PhantomReference类表示。如果一个对象与一个虚拟引用相关联,它可以在任何时候被垃圾收集器收集,就像没有引用与之相关联一样。请注意,虚拟引用必须与引用队列关联使用。当垃圾收集器准备回收一个对象时,如果它发现它仍然有一个虚拟引用,它会将这个虚拟引用添加到与之关联的引用队列中。程序可以通过判断虚拟引用是否已经被添加到引用队列中来知道被引用对象是否会被垃圾回收。如果程序发现一个虚拟引用已经被添加到引用队列中,它可以在被引用对象的内存被回收之前采取必要的措施。

  导入Java . lang . ref . phantom reference;导入Java . lang . ref . reference queue;public class Main { public static void Main(String[]args){ reference queue String queue=new reference queue String phantom reference String pr=new phantom reference String(new String( hello ),queue);system . out . println(pr . get());}}跨代引用跨代引用:即一代中的对象引用另一代中的对象。

  跨代引用假说:与同代相比,跨代引用只占少数。

  隐含推论:相互指称的两个物体应该倾向于同时生或死。记忆集:一种抽象数据结构,用于记录从非收集区到收集区的指针集。

  内存记录精读不考虑效率和成本。最简单的实现可以在非集合区域使用跨代引用的所有对象数组。但是空间成本高,成本高。

  在垃圾收集的场景下,收集器只需要通过内存集判断某个非收集区是否有指向收集区的指针,于是选择了一个更粗的粒度记录,包括字节精读、对象精读和卡片精读(卡片表)。

  字节精读:每条记录精确到一个机器字长,这个字节包含跨代指针。

  对象精度:每条记录都精确到一个对象,对象中有包含代际指针的字段。

  卡片精度:每条记录都精确到一个内存区域,内存区域中有带有代际指针的对象。

  卡表:是内存集的具体实现,定义了内存集的记录精度,与堆内存的映射关系等。卡最简单的形式可能只是一个字节数组。

  卡的每个元素对应于其标识的存储区域中特定大小的存储块,该存储区域称为卡页。写屏障(Write barrier)写屏障是在写对象引用(即引用赋值)之前或之后额外执行的逻辑。

  写屏障用于在对象状态改变时保持卡片表格状态。写屏障和内存集会在每次分配对象引用时产生一个写屏障中断操作,然后检查要写入的引用所指向的对象是否与该引用当前所指向的对象在不同的区域;如果不一致,则将相关参考信息通过CardTable记录在记忆集中;进行垃圾收集时,在GC根节点的枚举范围内加入记忆集,可以保证不需要进行全局扫描。

  第一步。如果可达性分析后发现一个对象没有连接到GC根的引用链,那么会第一次标记,筛选一次,筛选条件是这个对象是否需要执行finalize()方法。

  当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用时,虚拟机将这两种情况都视为“不需要执行”,直接标记第二次。

  两步之后,如果还是没用,就是垃圾。

  如果判断这个对象是执行finalize()方法所必需的,那么这个对象将被放在一个名为F-Queue的队列中,稍后由虚拟机自动建立的低优先级终结器线程执行。这里所谓的“执行”是指虚拟机会触发了这个方法,但并不承诺要等它运行完,因为如果一个对象在finalize()方法中执行的很慢,很可能会一直阻塞F-Queue队列,甚至导致整个内存回收系统崩溃。

  GC MinorGC/YoungGC型:新生代发生的一次聚集作用。

  MajorGC/OldGC: GC发生在老年。目前只有CMS收藏者可以单独收藏陈年。

  MixedGC:收集全新一代和部分老一代。目前,只有G1收藏家有这种行为。

  FullGC:收集整个Java堆和方法区域的GC。Stop-The-World机制Stop The WorldJava简称STW,是Java中的一种全局暂停现象,多由GC引起。所谓全局暂停,就是所有Java代码停止运行,原生代码可以执行,但是不能和JVM交互。

  危害是服务长时间停止,没有响应;对于HA系统,可能会造成主备切换,严重危害生产环境。

  目前垃圾收集器的优化方向基本上是最小化或缩短停界时间。JVM-停止世界:https://blog.csdn.net/jakeswang/article/details/107673734

  垃圾收集类型

  并发性:用户线程和GC线程同时执行(不一定并行,可以交替执行),不需要挂起用户线程,比如CMS。

  判断类无用条件

  标记-扫描:标记-扫描算法

  这是最基本的算法。就像它的名字一样,算法可以分为“标记”和“清除”两个阶段:首先标记所有需要回收的对象(比如哪个内存需要回收),标记后回收所有被标记的对象,如下图所示:

  优点:简单。

  缺点:

  一个是效率。打标和清洗效率不高。

  另一个是空间问题。标记清除遗憾可能导致大量不连续的内存碎片,导致后续无法分配大对象,再次触发垃圾回收。

  复制:复制算法

  为了克服标记清除算法的缺点,复制算法将可用内存容量分成两个相等的块,一次只使用一个块。当一个块的内存用完时,幸存的对象被复制到另一个块。然后清理一次使用的内存空间,如下图所示:

  优点:实现简单,效率高,不需要考虑内存碎片。

  缺点:使用的内存减少一半。

  现在商用虚拟机都是用这种收集算法来回收新一代。有企业分析,没有必要按照1:1的比例划分内存,因为新一代中的对象大多是“出生死亡”的。因此,热点虚拟机的Eden和Survivor的默认大小比为8:1。一个伊甸和两个幸存者,一次用一个伊甸和一个幸存者,也就是说只浪费了10%。如果其他幸存者都不能存储上次收集的对象,这些对象将通过“保证机制”进入老年。

  担保机制-分销担保

  当在新生代进行垃圾收集时,新生代的生活区无法放置或者新的大对象无法放置在伊甸园区,那么就需要在老年放置这些对象的策略,即老年会为新生代的GC做出空间分配保证。步骤如下:

  1.在MinorGC发生之前,JVM会检查旧时代的最大可用连续空间是否大于新时代所有对象的总空间,如果大于,就能保证M inorGC是安全的。

  2.如果小于,JVM将检查是否允许保证失败。如果允许,它将继续检查旧时代中的最大可用连续空间是否大于提升到旧时代的对象的平均大小。

  3.如果大于,请尝试MinorGC一次。

  4.如果它不大于,就应该进行一次完整的GC。

  标记压缩:标记排序算法

  复制算法的效率是建立在活对象少,垃圾对象多的前提下的。这种情况在年轻一代中经常发生,但在老一辈中更常见的是,大多数物体都是活体。如果仍然使用复制算法,复制的成本会很高,因为存活的对象很多。

  标记排序算法类似于标记清除算法。两者都是先标记回收的对象,然后不是直接清理对象,而是将所有幸存的对象都移到一端,然后直接清除端边界外的内存,如下图所示:

  世代收集算法

  逐代收集算法目前被大多数JVM垃圾收集器使用。其核心思想是根据对象的生命周期将内存划分为几个不同的区域。一般堆区分为老一代和年轻一代。老一代的特点是每次垃圾收集只需要收集少量的对象,而新一代的特点是每次垃圾收集需要收集大量的对象,所以可以根据不同代的特点采用最合适的收集算法。

  垃圾收集工

  垃圾收集算法只是内存回收的方法,垃圾收集器就是来实现这些算法,实现内存回收的。

  不同厂商、不同版本的虚拟机实现差异很大。下图显示了HotSpot中包含的收集器:

  注意:旧时代配备CMS收集器时,如果内存占用超过一定比例,系统会抛出并发模式失败,然后自动使用串行旧收集器进行全GC。

  1.红色虚线表示串行和CMS的组合以及ParNew和串行Old的组合在Jdk8中被放弃,在Jdk9中被完全放弃。

  2.当黄色虚线在Jdk14时,放弃并行扫码和串行旧码的组合。

  3.当绿色虚线在Jdk14中时,CMS垃圾收集器被完全废弃。

  垃圾收集器的发展近况

  Jdk1.7u4开始全面支持G1垃圾收集器。

  G1取代了CMS,成为Jdk9中默认的垃圾收集器。(CMS被宣布过时)。

  Jdk10是G1垃圾收集器,它实现了并行性以改善最坏情况下的延迟。

  Epsilon垃圾收集器,也称为无操作收集器,是Jdk11中引入的。同时介绍了Oracle公司的可扩展低延迟垃圾收集器ZGC(Z垃圾收集器)。

  Jdk12增强了G1垃圾收集器,可以自动将未使用的堆内存返回给操作系统。同时介绍了OpenJDK,并介绍了Red Hat公司开发的Shenandoah GC低延迟垃圾收集器(实验阶段)。

  Jdk13增强了ZGC,可以自动将未使用的堆内存返回给操作系统。

  Jdk14完全放弃了CMS垃圾收集器(如果显式设置,会提示警告,但不会中断。默认收集器将是G1)。扩展了ZGC在MacOS和Windows中的应用。

  1.串行垃圾收集器

  串行垃圾收集器有串行和串行旧两种,一般一起使用。是一个单线程收集器,当垃圾被收集时,它停止运行。

  好处很简单。对于单个CPU来说,可能效率更高,因为没有多线程的交互开销。它是客户端模式下默认的新一代收集器。

  在并发性好的cpu环境下,其暂停时间比串行收集器短,但对于单个cpu或并发性弱的CPU,由于多线程的交互开销,可能比串行收集器差。

  默认的收集线程数与cpu数相同,运行数可以通过修改-XX:ParallelGCThreads来设置。用于新一代收集和复制算法。

  新一代并行清除收集器:它是新一代的并行收集器,使用复制算法。

  和ParNew类似,但更注重吞吐量,吞吐量第一,吞吐量=代码运行时间/(代码运行时间垃圾收集时间),即高效利用cpu时间,尽快完成程序的运行任务。您可以设置最大暂停时间MaxGCPauseMillis和吞吐量GCTimeRatio。

  打开-XX: UseParallelGC,默认情况下,在服务器模式下提供与SerialOld匹配的生成收集方法。

  通过-XX: UseParallelOldGC参数使用并行清除并行旧设备的组合来执行内存收集。

  -XX: UseAdaptiveSizePolicy:如果设置了该参数,则表示新一代的大小、Eden、幸存者比率等。将随GC一起动态调整,以提供最合适的暂停时间或最大吞吐量。

  4.CMS收集器

  CMS(并发标记-清除)收集器的目标是获得最短的恢复暂停时间。从名字开始,直到把它交给标记清除算法。但是,它比一般的标记清除算法更复杂,可以分为以下四个阶段:

  初始标记:标记GC根可以直接关联的对象会“停止世界”。

  并发性:GC根跟踪,可以与用户线程并发执行。

  重新标记:重新判断标记时生成的对象的存活情况,修正这些对象的标记,执行时间比并发标记短,会“停世”。

  并发:清除对象,这些对象可以与用户线程并发执行。

  2.无法处理处理过程中生成的垃圾,这可能会导致FullGC。

  3.因为它是基于标记-清除算法,所以无法避免空间碎片的产生。CMS收集器无法处理浮动垃圾,可能会出现“并发模式故障”,导致另一次完全GC。

  所谓浮动垃圾,当用户线程还在CMS并发清理阶段运行时,自然会随着程序的运行产生新的垃圾。这部分垃圾出现在标记过程之后,CMS无法在当前的集合中处理它们,所以只能在下一次GC时清理。

  参数设置

  -XX:useconcmasweepgc:使用ParNew CMS Serial Old的收集器组合。Serialod将充当CMS错误的备份收集器。

  -XX: CMSINITIONGCUPANCYFRACTION:指定垃圾收集前,旧代在当前年份中有多少空间是满的。默认值为80%。

  -xx:usecmscompactfullcollection:(默认情况下打开)当CMS收集器无法保存FullGC(需要STW)时,启动内存碎片整理过程。

  -XX:cmsfullgcsbeforecumption:指定完成前的FullGC次数。

  -XX:ParallelCMSThreads:指定CMS回收线程数,默认为:(CPU数3)/4。

  G1收藏家

  G1被单挑出来的原因是它很复杂。它在JDK 1.7中被确立为项目目标,在JDK 7u2之后发布,并在JDK 9中成为默认的垃圾收集器。您可以通过启动参数"-XX: UseG1GC "来指定G1 GC的使用。

  G1整体上是基于标记清除算法的,但局部是基于复制算法的。这意味着它的空间整合性更好,因为不会有空间碎片。

  G1是并发和并行的,它可以充分利用多CPU、多核的硬件环境来缩短“停止世界”的时间。

  G1仍然被几代人收集,但G1不再像上面提到的垃圾收集者。它需要按代与不同的垃圾收集者合作,因为G1的垃圾收集区是“区域”。G1的分代收集与上面的垃圾收集器不同,除了年轻一代的ygc和全堆扫描的fullGC之外,还有一个包含所有年轻一代和一些旧区域的MixedGC。

  G1还可以预测暂停,并通过调整参数来设置垃圾收集的最大暂停时间。

  跟踪G1地区垃圾场的价值,在后台维护一个优先级列表,每次按照允许的时间回收价值最高的地区,保证在有限的时间内高效收集。

  G1收集器的操作大致可以分为以下几个步骤:初始标记、并发标记、最终标记、筛选和回收。

  初始标记阶段:只需标记GC根可以直接关联的对象,并修改TAMS的值(标记集的下一个顶部),这样下一阶段用户程序并发运行时,就可以在正确可用的区域创建新的对象。这个阶段需要STW,但需要的时间很短。

  标记阶段:从GC根开始,分析堆中对象的可达性,找到幸存的对象。这个阶段需要很长时间,但是它可以与用户线程并发运行。

  最后的打标阶段:是对并发打标期间由于用户程序持续运行而发生变化的那部分打标记录进行修正。虚拟机将这段时间内的对象变化记录在线程记忆集日志中,最终标记需要将记忆集日志的数据合并到记忆集中。在这个阶段,线程需要挂起,但是可以并行执行。

  筛选阶段:首先对各区域的回收价值和成本进行排序,根据用户期望的GC暂停时间确定回收方案。

  G1采集器的操作示意图如下图所示。

  G1分割的概念

  在世代的基础上,G1堆区引入了划分的概念。G1堆被分成几个区域,下面和“分区”代表同一个概念。(这些分区不需要连续的内存空间。)区域的大小可以通过G1HeapRegionSize参数设置,该参数必须是2的幂,允许的范围是1Mb到32Mb。JVM会根据堆内存的初始值和最大值的平均值来计算分区大小,平均堆大小会划分成2000个左右的区域。一旦设置了分区大小,它在启动后将不会改变。下图简单描述了下G1分区模型。

  伊甸园地区(年轻一代-伊甸园地区)

  幸存者区域(年轻一代-幸存者区域)

  旧地区(老年)

  巨大区域(巨型物体区域)

  空闲区域(未分配区域,也称为可用分区)——上图中的空白区域。

  G1的巨型物体指的是占据该地区50%以上容量的物体。一个巨大的区域专门用来储存巨大的物体。如果一个巨大的对象无法放入一个H区,它将被几个连续的H区存储。因为巨型对象的转移会影响GC的效率,所以在并发标记阶段发现巨型对象已经不存在时,会直接回收。Ygc在某些情况下也会回收巨型物体。

  分区可以有效利用内存空间,因为整个集合采用“标记-排序”,区域基于“复制”算法。在GC之后,幸存的对象将被复制到可用的分区(未分配的分区),因此不会有空间碎片。

  G1参数设置:

  -XX: UseG1GC:使用G1(垃圾优先)垃圾收集器。

  -XX:MaxGCPauseMillis=n:设置最大GC暂停时间指示器(target),这是一个软目标。JVM将尽最大努力实现软目标。

  -xx:initiationheapocupancypercent=n:堆空间被占用时触发GC(百分比)。默认值为45。

  -XX:NewRatio=n:新一代与老一代的大小比(新/老一代)。默认值为2。

  -xx:幸存者比例=n:Eden/幸存者空间大小比例,默认值为8。

  -XX:MaxTenuringThreshold=n:对象从新世代到旧世代的年龄,默认值为15。

  -XX:ParallelGCThreads=n:设置垃圾收集器在并行阶段使用的线程数。默认值因JVM运行的平台而异。

  -XX:congcthreads=n:并发垃圾收集器使用的线程数。默认值因JVM运行的平台而异。

  -xx: G1保留百分比=n:设置保留内存作为空闲空间的百分比,以降低目标空间溢出的风险。默认值为10%。

  -xx: G1堆大小=n:设置G1区域的大小,取值为2的幂,取值范围为1MB到32MB。目标是根据最小的Java堆大小划分大约2048个区域。

  G1的分类和加工

  JDK10之前G1的GC只有年轻GC和混合GC。完整的GC处理交给单线程串行旧垃圾收集器。

  Gc YoungGC世代系列

  在分配一般对象(非巨型对象)时,当所有Eden区域使用率达到最大阈值,无法申请足够内存时,将触发一次YoungGC。每次YoungGC都会回收所有的伊甸园和幸存者区域,将幸存的对象复制到旧区域和另一部分幸存者区域。旧区的标准是PLAB的计算结果。YoungGC会停止世界,因为它会做根扫描。

  YoungGC的回收过程如下:

  MixGC混合集合

  MixedGC是G1 GC独有的。与完全GC不同,混合GC只回收一些旧的区域。有许多参数来控制哪个旧区域可以放入CSet。例如,在youngGC之后的G1HeapWastePercent参数可以允许堆垃圾的百分比。超过该值将触发mixedGC。

  G1MixedGCLiveThresholdPercent参数控制旧分区中幸存对象的比率。当达到阈值时,旧分区将被放入CSet。

  MixedGC通常发生在第一次YoungGC之后。为了提高效率,MixedGC会重用YoungGC的全局根扫描结果,因为这个Stop the world进程是必须的,整体上缩短了暂停时间。

  MixGC的恢复过程可以理解为YoungGC之后附加的全局并发标记。全局并发标记主要用于处理旧区(包括H区)的活体标记,流程如下:

  最初的标记。标记GC根,STW,一般重用YoungGC的停顿时间。如前所述,initial标记设置所有分区的NTAMS值。

  根分区扫描(RootRegionScan)。在这个阶段,GC的线程可以与应用程序线程并发运行。它主要扫描初始标记和之前YoungGC对象转移到的幸存者分区,并标记幸存者区域中引用的对象。因此,这个阶段的幸存者分区也称为RootRegion。

  ConcurrentMark(并发标记)。非完全空闲的所有分区的幸存对象将被并发标记,即使使用SATB算法,每个分区也将被标记。

  最后的标记(备注)。主要的SATB缓冲区以及在同步标记阶段没有标记的逃逸鱼(活体)将在STW处理。请参考上面的SATB处理。

  清理阶段(Clean UP)。上面SATB还提到会交换位图,和交换PTA ms PTAMS,NTAMS。整理堆分区,调整相应的RSet(例如,如果卡中记录的所有对象都被回收,卡也将从RSet中移除)。如果识别出一个完全空的分区,这个分区的RSet将被清除。这个过程将是STW。

  在清理阶段之后,幸存的对象将被转移(复制算法)到其他可用分区,因此当前分区成为新的可用分区。转移主要是解决分区中的碎片问题。

  FullGC

  当G1对象复制/传输失败或无法分配足够的内存时(例如,大型对象没有足够的连续分区分配),FullGC将被触发。FullGC采用单线程串行旧模式stop the world,所以一旦触发FullGC,STW就会应用线程,执行效率较慢。JDK 8版本中的G1不提供完整的GC处理。对于G1 GC的优化,大目标是没有FullGC。

  ZGC收藏家

  ZGC是JDK11中新增加的一个实验性低延迟垃圾收集器,目前仅支持Linux/x86-64。

  ZGC的设计目标是支持TB内存容量,暂停时间低(10ms),对整个程序吞吐量的影响小于15%。

  ZGC收集器是一个基于区域内存布局的垃圾收集器,它(暂时)是无代的。它使用读屏障、染色指针和内存多重映射等技术实现并发标记-排序算法,其首要目标是低延迟。

  GC性能指数

  吞吐量:应用程序花费在非GC上的时间百分比,应用程序代码的执行时间/总运行时间。

  GC负载:与吞吐量相反,它指的是应用程序花费在GC上的时间百分比,即GC时间/总运行时间。

  暂停时间:应用程序在GC stop-the-world中花费的时间。

  垃圾收集频率:指一个时间段内垃圾收集发生的次数。

  反应速度:一个物体从变成垃圾到被回收的时间。

  交互式应用要求暂停时间越短越好。

  JVM内存容量分配原则

  对于老年期响应时间有限的应用程序,通常在老年期使用并发收集器,因此其大小应考虑并发和持续时间等参数。

  如果设置过小,可能会造成内存碎片,高回收频率会造成应用暂停。

  如果设置较大,会增加回收时间。

  对于陈年,对于吞吐量优先的应用:通常设置较大的新一代和较小的陈年,尽量回收大部分短期对象,减少中期对象,陈年尽量存储长期存活的对象。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: