当前位置: 首页 > 后端技术 > Java

对于大促准备过程中出现的fullGc,我们可以做些什么来分析呢?

时间:2023-04-02 10:11:02 Java

作者:京东科技白杨前言:背景:为了应对618和双11大促,消费金融端会根据零售端大促的节奏进行整体系统准备。对核心流量入口承载的系统进行加固优化,消除系统风险,确保推广期间系统稳定性。大促期间,消费金融业务面临与用户面对面、高并发、系统风险大概率直接导致资金流失风险等问题。在日常压测和大促过程中,经常会出现JVM存在大量youngGC和部分fullGC,导致性能下降,可用性降低的情况。之前对Jvm的垃圾回收机制不是很熟悉,如何规避,如何调优,基本上什么都不懂,这篇文章也是对所学知识的巩固~1.什么是GC虚拟机?JVM(Java虚拟机)。JVM是Java程序的虚拟机,是实现Java语言的解释器。它提供了一个独立于操作系统的运行环境,使得Java程序可以在任何支持JVM的计算机上运行。JVM负责Java字节码的加载、校验、解释、执行和垃圾回收,为Java程序提供内存管理、线程管理、安全控制等服务。JVM中的GC(GarbageCollection)是garbagecollection的缩写,是JVM的内存管理机制。YoungGC和FullGC是两种不同的GC算法。YoungGC:新生代对象的回收算法,通常使用复制算法或标记算法。因为新生代中的对象生命周期短,所以YoungGC比FullGC快很多。FullGC:一种针对整个堆内存的回收算法,用于回收那些在YoungGC中没有回收的存活对象。FullGC比较慢,因为它需要扫描整个堆内存,所以对系统性能的影响比较大。因此,在设计Java应用程序时,需要尽量减少FullGC的次数,以保证系统性能。常用的方法有扩大新生代的内存空间,减少数组长度等。以上基本上就是对Jvm和Gc的一个大概的解释。但是可以很明显的看到少了一些细节,对我们来说还是没用的。考生应该如何理解具体的场景??我们先来了解一下youngGC的诞生过程:首先了解copy算法和markfinishing算法,这是两种不同的YoungGC回收算法。复制算法:将新生代内存分成大小相等的两部分。新创建的对象存储在一个部分,而另一部分用于存储存活的对象。当新生代内存不够时,就会发生YoungGC,将存活下来的对象复制到另一个内存区域。复制算法不会造成内存碎片,但会消耗一定的内存空间。标记排序算法:每次YoungGC时,首先标记所有存活的对象,然后将所有未存活的对象一起排序。因此,内存碎片会导致空间浪费。标记算法适用于需要保持内存空间整洁的应用程序,例如那些长时间运行的服务器应用程序。看看这个。本质上,YoungGc可以理解为jvm正常的垃圾清理过程。根据上面的解释,相信聪明的朋友可以清楚的看出,youngGc的回收效率更高,对业务端的影响更大。小多了~所以,我们来仔细看看这个让人头疼的fullGc,它是怎么来的?FullGC是FullGarbageCollection的缩写,指的是扫描整个堆内存,回收不用的对象,整理内存的过程。由于堆内存的整体回收过程非常缓慢,FullGC会导致应用程序暂停。上面说了,只有更合理的内存分配,避免未使用对象的频繁出现,调整堆内存的扫描时间。FullGC,全垃圾回收,是一种垃圾回收过程,暂停所有应用线程,回收整个堆。(这太可怕了。。。)初始标记:首先,垃圾收集器标记哪些对象需要被回收。并发标记:垃圾收集器然后将标记任务分发给多个线程,这些线程并发执行标记任务。重标记:在并发标记过程中,如果创建了新的对象,需要对这些对象进行重标记。整理:接下来,垃圾收集器将未标记的对象整理到内存的一端。回收:最后,垃圾收集器回收标记的对象并释放内存。放一张图给大家理解~FullGc的生命过程~本质上就是垃圾太多,无法正常工作,内存空间不够用。你必须停止一切并进行一般清洁。2.写代码Whatcanyoudowhen从上面可以看出,fullGc是非常可怕的,因为堆内存的整体回收过程非常缓慢,因此,FullGC可能会导致应用挂起,直接崩溃。..为了避免FullGC的发生,合理设置系统堆内存的大小和优化代码是必不可少的。基本上有以下技巧:?调整堆内存的大小:确定合适的堆内存大小,避免FullGCkey的发生。?代码的内存优化:使用不同的数据结构,避免内存泄漏,使用对象池和其他技术。?使用更大的新生代:新生代是一块内存区域,存放短命对象,更大的新生代可以降低FullGC的频率。?设置合适的垃圾回收算法:使用G1GC算法等技术可以提高系统性能并降低FullGC的频率。?这些是避免FullGC发生的一些常见建议。请注意,每种情况都不同,因此请根据您的情况选择合适的方法。这些方法看起来还是很抽象。。。举个具体的例子吧首先,堆内存的大小和垃圾回收算法都不是我们可以操作和关心的,业务方一般也不太擅长调整,所以就交给运维同学了。简单介绍,调整内存大小:通过调整JVM参数,如-Xms,-Xmx,适当增加内存。具体我们可以做什么,最重要的是减少数据对象的生命周期:通过使用引用弱引用、软引用、幻象引用等类型,当不需要数据对象时,可以直接回收数据对象,从而避免FullGC。缩短数据对象的生命周期,是指在程序中使用对象时,尽量缩短对象的生存时间。这样可以减少垃圾对象的数量,降低FullGC的频率。这是我们需要重点关注的!!以下是一些具体的例子:1.避免使用不必要的临时对象:如果程序中有大量的临时对象,它们可能很快就会被垃圾收集器清理掉。因此,你应该避免创建不必要的临时对象来减少对象的生命周期。例如:doubleaverage(double[]values){doublesum=0;for(doublevalue:values){sum+=value;}returnsum/values.length;}在这个例子中,数组values是一个临时对象,在函数结束时会被销毁。这样,您就不必考虑如何删除集合,避免了内存泄漏的风险。另外,Stringconcatenate(Liststrings){Stringresult="";for(Stringstr:strings){结果+=str;}returnresult;}在此示例中,每个循环创建一个临时String对象并将其附加到结果。随着循环的进行,这些临时对象可能会堆积起来,导致频繁的GC操作。为了避免这个问题,可以使用Java中的StringBuilder来构建字符串Stringconcatenate(Liststrings){StringBuilderresult=newStringBuilder();for(Stringstr:strings){result.append(str);}returnresult.toString();}这样就不需要再创建一个临时的string对象,从而减少了GC的次数。2、尽快释放对象:当对象不再需要时,应尽快释放,以便及时回收。例如,在程序完成处理后立即释放一个对象,而不是等到下次需要使用它时。比如我们最常用的for循环是great,for(inti=0;ipool=newArrayList<>(POOL_SIZE);static{for(inti=0;iweakRef=newWeakReference<>(obj);对象=空;系统.gc();if(weakRef.get()!=null){System.out.println("对象还活着");}else{System.out.println("对象已被垃圾回收");}}}举一个更具体的点:importjava.lang.ref.WeakReference;importjava.util.HashMap;publicclassWeakReferenceExample{publicstaticvoidmain(String[]args){HashMap>缓存=新的HashMap<>();MyObjectobj=newMyObject("示例");cache.put("例子",newWeakReference<>(obj));对象=空;系统.gc();System.out.println(cache.get("例子").get());}静态类MyObject{字符串na我;publicMyObject(Stringname){this.name=name;}@OverridepublicStringtoString(){return"MyObject{"+"name='"+name+'''+'}';}}}this例子中,我们将一个MyObject对象封装在一个弱引用中,保存在HashMap缓存中。当我们显式调用System.gc()方法时,JVM会尝试回收这些未使用的对象。如果内存不足,那么MyObject对象就会被回收,那么cache.get("example").get()就会返回null3.测试能做什么回顾一下全文,其实我们用的不多能做的,只有在业务代码测试的过程中,注意对象的使用频率,拒绝无效的引用或者new很多不需要的对象。具体手段:定时监控GC日志:通过我们的jvm关注,在一个大项目上线后,或者代码改动特别大的项目上线后,做一些读写压力测试操作~关注我们的jvm,jvm监控地址数据结构优化:根据上述手段,测试开发工程师可以通过上述手段优化数据结构,减少数据对象的生命周期,从而避免FullGC。测试过程中,注意数据结构的合理性~注意单元测试:通过运行开发好的单元测试,或者手动编写一个,来模拟实际的内存使用情况,评估内存使用情况(基本上就是当前业务代码可以运行,大概率是没有问题的,,)总结:日常业务代码测试对内存比较敏感。没有错误可能不会导致问题。现在我们的系统成熟可靠。不过面对大促的压力,如果能提前解决隐患,摆脱内存占用的风险,也能节省我们压测时的工作量~