jvm随笔4-垃圾收集器

1.可达性分析时一致性确保

1.1 GC停顿

在确定对象是死亡还是存活时,需要进行可达性分析,而在进行可达性分析时需要进行GC停顿来确保一致性(如果不停定,导致引用持续变化,就无法准确定位对象的存活和死亡情况)。
而在进行GC停顿以后进行检查时,不需要一个不漏全部检查所有上下文和引用(不需要挨着查询全文来找对象索引),虚拟机使用一个OopMap的数据结构使虚拟机直接得知什么地方存放着对象引用(类加载完成后HotSpot把对象内在什么偏移上存放着怎样的数据计算出来),也会在特定位置上记录栈和寄存器中那些位置是索引。这样就保证了GC在扫描时可以直接得到这些索引,不需要自己去遍历全文寻找。(就像文中的重点已经被勾画出来并使用数据结构逐个记录重点在哪里,使用时就不需要读者自己再去读全文找重点,可以通过数据结构标识的地方直接找到。耗费空间让时间复杂度:O(n)–>O(1))

1.2 安全点

但很现实的一个问题,实际使用时,导致引用关系变化的指令非常多(从而导致OopMap内容发生变化),如果为每一条指令都生成一个OopMap显然是不合理的,这样需要大量的额外空间,使得GC空间成本极高(jvm并不打算引用变化一次就修改一次OopMap,而是每累计一段时间统一修改一次)。
事实上,jvm只在特殊的位置生成OopMap,这些地方称为安全点,所以说程序不是到任意位置都能进行GC,只有在安全点上才能开始GC,而安全点的数量要适宜(过少导致GC执行间隔过大,过多导致运行负荷增加),一般在方法调用,循环跳转,异常跳转这样的指令复用的地方产生安全点
而在多线程中,进行GC时要确保所有线程执行的程序都在安全点上,有两种方案来确保:

  • 抢先式中断:所有线程全部中断,不在安全点的线程恢复执行,直到跑到安全点(基本不用抢先式中断)(要GC时全部立即喊停,没到安全点的让自己再跑一下)
  • 主动式中断:为所有安全点设立标志,让线程主动去询问标志,若标志为真(要进行GC就把标志置真)则将自己中断挂起(要GC时让在安全点的工作人员开始工作,拦住过路的线程)
1.3 安全区域

使用安全点并不能完美解决问题,若线程处于阻塞或者挂起状态,无法响应中断,这时候就需要安全区域(安全点是点,安全区域就是线),安全区域内是一段代码片段,在这个片段内,引用关系都不会发生变化,进行GC都是安全的。
在线程进入安全区域后,会标记自己进入安全区域,在GC时不需要管这些线程,在线程出安全区域时,会主动询问GC是否完成,若完成就直接出去,未完成就阻塞等待。

2.垃圾收集器

垃圾收集器是内存回收的具体实现(各种垃圾收集器没有最好只有最合适)

2.1 Serial收集器

最基本的收集器,单线程的收集器, 采用复制算法,不仅是只使用单cpu单线程去回收垃圾,并且在回收时要暂停所有的工作线程,Serial收集器的优点是其简单而高效(同其他收集器的单线程相比,因为Serial收集器没有线程交互等的开销)。

2.2 ParNew收集器

多线程版本的Serial收集器,其目前只能配合Serial收集器和CMS收集器,由于其相对于Serial收集器,存在线程交互开销,所以在cpu较少时,其效率是低于Serial收集器的,当然随着CPU的增加,效率也会提高。

2.3 Parallel Scavenge收集器

使用复制算法,前面两种收集器,都有一个目标,就是减少每次GC的时间,但都是从软件方面降低时间,而Parallel Scavenge收集器通过减少GC一次回收的量来降低一次GC的时间,Parallel Scavenge收集器可以设置一次GC的停顿时间和吞吐量,在执行时通过控制每次垃圾的回收量来确保一次GC的时间,但**每次的回收的量过少会导致吞吐量的降低和空间不足。**吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)

2.4 Serial Old收集器

Serial的老年代版本,也是单线程的,使用标记-整理算法。
用途:

  • 与Parallel Scavenge收集器搭配使用
  • 作为CMS的后备预案(CMS回收会导致可用空间零散分布)
2.5 Parallel Old收集器

Parallel Old是Parallel Scavenge的用于回收老年代的版本使用多线程和标记-整理算法,产生的原因是因为Parallel Scavenge只能与Serial Old配合,而Serial Old单线程拖累了性能,所以就产生了Parallel Old来配合Parallel Scavenge。在注重吞吐量及CPU资源敏感的场合,适合使用Parallel Old+Parallel Scavenge的组合。

2.6 CMS收集器

CMS收集器使用标记-清除算法,整个过程分为四步,其中两步(并发标记与并发清除)允许工作线程一起,且这两步工作时间较长,使得GC真正停顿的时间显著减少。

  • 初始标记:只标记GC Roots直接关联到的对象,这个过程要使得所有工作线程停顿
  • 并发标记:进行GC Roots Tracing,允许工作线程一起运许
  • 重新标记:工作线程的运行会导致标记发生变动,这一步用于修正这些变动的标记。
  • 并发清除:开始垃圾回收,可以与工作线程一起执行

CMS的缺点:

  • 并发标记和并发清除的时候占用了一部分线程从而导致应用程序变慢,在cpu数量较少时,回收会占用大量的cpu资源,在cpu数量增加以后,占用资源百分比会减少。在cpu较少时,会让GC任务与用户任务,在cpu中交替执行(模拟多任务但cpu抢占式),从而减少GC对程序的影响。
  • 由于并发清除阶段运行用户程序一起执行,所以新产生的垃圾在本次无法回收,只有等到下一次,称为无法回收浮动垃圾,所以每次需要预留空间给用户线程使用(因为GC和用户程序一起执行,如果等到满了再GC,那么用户程序就没有空间了),若预留空间较多,就会使得GC发生频率变高,若预留空间过少,可能导致剩余内存无法满足程序需要。就会导致虚拟机启动后备预案,临时使用Serial Old收集器来进行垃圾收集,但这个收集是单线程的,且会使得用户程序全部停止,会使得性能下降。
  • 使用标记-清除算法导致可用空间零碎分布,若出现大的对象无法分配内存时,导致提前触发Full GC,而CMS收集器在进行FullGC是会进行碎片整理。
2.7 G1收集器

特点:

  • 并发与并行:G1充分利用多CPU、多核环境下的硬件优势,可以缩短GC停顿的时间,并且部分步骤(其他收集器要进行停顿)可以与工作进程并发
  • 分代收集:分代概念在G1中仍有保留,独立管理整个GC堆。
  • 空间整合:使用标记-整理算法,回收时不会产生可用空间零碎分布的现象。
  • 可预测的停顿:与CMS一样,都追求降低停顿,但其还能预测停顿的时间模型,预测停顿的时间

G1的内存布局和其他收集器的差别很大,把java堆划分为了很多大小相等的独立区域(Region)(类似分页),同时也保留了老年代与新生代的隔离,但不再是物理隔离。

GC时G1还会对各个Region进行优先级划分(优先级列表),回收时根据优先级来确定回收顺序。而这样化整为零的思想实现细节比较困难,一个区域里面的对象是可以与其他区域的对象建立引用关系的,那么就不能以一个区域为单位进行了。

即使如此,在可达性分析时也不用扫描整个java堆。虚拟机使用Remembered Set来避免全堆扫描,每个区域都有自己的Remembered Set。在对reference类型数据进行写时,先中断写操作,然后检测,引用的对象是不是其他Region的,如果是就会把被引用对象的区域记录在本Region的Remembered Set中。所以进行可达性分析时,根据优先级选择Region,进行扫描,接着扫描该Region的Remembered Set中存储的区域信息。

G1的步骤分为四步

  • 初始标记:类似CMS,标记GC Roots能直接关联到的对象,需要进行GC停顿,然后标记区域使得下一次并发时,用户进程找到正确的Region进行新对象创建。
  • 并发标记:类似CMS,进行可达性分析,寻找要回收的对象,耗时较长,可并发执行用户进程
  • 最终标记:修正并发标记期间,用户进程并发执行引起的引用关系变化,变化消息记录在线程Remembered Set Log中,需要把数据合并到Remembered Set中,这阶段要停顿,可并行执行。
  • 筛选回收:根据优先级来逐个进行区域的回收。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JVM是一种虚拟机(Java Virtual Machine),能够在不同操作系统和硬件平台上运行Java程序。而G1垃圾收集器JVM中的一种垃圾收集算法,可以处理大型Java堆。 使用G1垃圾收集器能够提高应用程序性能,因为它采用分区技术和优先级调度算法,可以根据应用程序的使用情况,动态地分配堆空间分区,从而减少垃圾收集时间和暂停时间。 下面是使用G1垃圾收集器来优化JVM的步骤: 第一步,设置JVM参数,例如开启G1垃圾收集器,设置堆空间大小等。 第二步,使用JVisualVM或其他工具,对应用程序进行分析,确定是否存在内存泄漏或内存过度消耗的现象。如果有问题,则需要对应用程序进行修改,以降低内存使用率。 第三步,对应用程序进行压测,确定最大并发并适当调整G1垃圾收集器参数,例如设置初始停顿时间、最大停顿时间、空闲时间等。 但需要注意的是,使用G1垃圾收集器时需要事先评估应用程序的运行环境和特性,例如硬件配置、每秒请求计数、响应时间等。只有在合适的环境下,G1垃圾收集器才能发挥最佳效果。同时,应用程序也需要符合一些条件,例如堆空间不能太小,内存消耗不能太大等。 总之,通过优化JVM,使用G1垃圾收集器能够提高Java应用程序的性能和可靠性,减少停顿时间。但使用时需要仔细评估和测试,确保最佳效果。 ### 回答2: JVM是Java虚拟机的缩写,是Java运行环境的核心部分。垃圾收集器JVM的一个重要组成部分,负责回收程序的无用对象和内存。G1垃圾收集器JVM中的一种,是目前比较流行的一种。下面介绍G1垃圾收集器的优化使用方法。 首先,G1垃圾收集器最大的优点是能够充分利用多核处理器和大内存的优势。因此,在使用G1垃圾收集器时,需要配置合适的垃圾回收线程数和堆内存大小。通常情况下,推荐将垃圾回收线程数设置为CPU核心数的一半左右,而堆内存大小则应根据应用程序的实际情况设定。 其次,G1垃圾收集器还支持增量垃圾回收和并发标记。这意味着垃圾收集器可以在应用程序执行的同时,进行部分的垃圾回收和标记操作。这有利于减少垃圾回收对应用程序执行的影响。 再次,G1垃圾收集器还支持混合模式。这种模式下,垃圾收集器会将堆内存分为多个区域,并按照各个区域的使用情况决定垃圾回收的策略。一些非常重要的区域可以在短时间内进行Full GC,而其它区域则可以使用增量式垃圾回收策略。这一功能可以进一步提高垃圾回收的效率。 最后,在使用G1垃圾收集器时,还需要注意一些细节问题。例如,需要选择合适的回收阈值、合理配置回收周期等等。此外,一些调试工具如JConsole、JVisualVM等也可以帮助开发者监控和调整G1垃圾收集器的性能。 总之,G1垃圾收集器是目前JVM中性能较优异的垃圾收集器之一。在使用G1垃圾收集器时,需要根据实际情况合理配置线程数、堆内存大小和一些参数,同时关注混合模式、增量垃圾回收和并发标记等特点,才能充分发挥其性能优势。 ### 回答3: JVM是Java虚拟机的缩写,它是Java程序运行的环境,它提供了一个平台无关的执行环境,能够提高Java应用程序的效率和安全性。Java应用程序的性能优化是一个复杂的过程,其中之一的重要方面是垃圾收集器的优化。 G1是JVM中的一种垃圾收集器,它主要用于大堆内存应用程序,它在处理垃圾回收时可以实现高吞吐量和低延迟,而且它可以管理大容量的内存,能够处理高并发情况下的大量垃圾回收。 G1垃圾收集器具有以下优点: 1.低延迟:G1垃圾收集器能够有效地降低应用程序的延迟,因为它是一个分区式的垃圾收集器,它会将内存分成多个区域,每个区域都有自己的垃圾回收时间,这样可以最大程度的减少垃圾回收的时间。 2.高吞吐量:G1垃圾收集器能够管理大容量的内存,能够处理大量的垃圾回收,因此它的吞吐量很高。 3.可预测性:G1垃圾收集器能够实现可预测性垃圾回收,这意味着它可以为每个应用程序分配一个确定的垃圾回收时间,从而避免出现系统性能波动的情况。 4.自适应:G1垃圾收集器能够根据内存的使用情况来自动调整垃圾回收的策略,从而最大程度地优化垃圾回收的效率和性能。 在使用G1垃圾收集器时,需要注意以下几点: 1.适当配置参数:需要根据应用程序的实际情况,适当配置G1的参数,以提高垃圾回收的效率和性能。 2.避免频繁Full GC:G1垃圾收集器能够尽量避免Full GC,但是在一些场景下,还是会出现Full GC的情况,因此需要尽量避免频繁Full GC的情况出现。 3.避免内存泄漏:G1垃圾收集器虽然能够处理大量垃圾,但是它无法处理内存泄漏的情况,因此需要及时发现和解决内存泄漏的问题。 总之,G1垃圾收集器是一个高性能、高效率的垃圾回收器,能够满足大堆内存应用程序的垃圾回收需求。在使用过程中,需要根据实际情况适当配置参数,避免频繁Full GC和内存泄漏的发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值