首页 > 其他分享 >FullGC详解

FullGC详解

时间:2024-03-01 09:01:08浏览次数:30  
标签:Full 对象 回收 FullGC GC 内存 使用 详解

前言:


背景:


一、什么是 JVM 的 GC?

JVM(Java Virtual Machine)。JVM 是 Java 程序的虚拟机,是一种实现 Java 语言的解释器。

 

它提供了一种独立于操作系统的运行环境,使得 Java 程序在任何支持 JVM 的计算机上都可以运行。JVM 负责加载、验证、解释、执行和垃圾回收 Java 字节代码,并为 Java 程序提供内存管理、线程管理和安全控制等服务。

 

JVM 中的 GC(Garbage Collection)是垃圾回收的缩写,是 JVM 的内存管理机制。

 

Young GC 和 Full GC 是两种不同的 GC 算法。

 

Young GC:针对新生代对象的回收算法,通常使用的是复制算法或者标记整理算法。因为新生代中的对象生命周期短,所以 Young GC 速度要比 Full GC 快得多。

 

Full GC:针对整个堆内存的回收算法,用于回收那些在 Young GC 中没有回收的存活对象。Full GC 速度比较慢,因为它需要扫描整个堆内存,因此对系统的性能影响较大。

 

所以在设计 Java 应用时,需要尽量减少 Full GC 的次数,以保证系统的性能。常见的方法包括扩大新生代的内存空间,减少数组长度等。

 

以上基本是通用的对 Jvm 和 Gc 的解释。但是可以明显看出缺少一些细节,对我们来说还是没什么用,测试同学该如何理解具体的场景呢??

 

我们首先来理解 young GC 的诞生过程:

 

首先, 理解复制算法和标记整理算法,它们是两种不同的 Young GC 回收算法。

 

复制算法:将新生代内存分成两个等大的部分,新创建的对象存储在一个部分,而另一个部分用于存储存活的对象。当新生代内存不够用时,Young GC 会发生,将存活的对象复制到另一个内存区域。复制算法不会导致内存碎片,但是会消耗一定的内存空间。

 

标记整理算法:每次 Young GC 时,会先标记所有存活的对象,然后再将所有不存活的对象整理到一起。因此,内存碎片可能会导致空间浪费。标记整理算法适用于需要保持内存空间整洁的应用,比如那些需要长时间运行的服务器应用。

 

这个看看就好,本质上 Young Gc 可以理解成 jvm 正常的扫垃圾过程

 

根据上述的解释,相信聪明的小伙伴可以清晰的看到,young Gc 有着更高的回收效率,对业务侧的影响要小的多~因此,我们进一步来看看头痛的 full Gc,是怎么来的?

 

Full GC 是 Full Garbage Collection 的缩写,是指把整个堆内存扫描一遍,回收不再使用的对象并且整理内存的过程。由于堆内存的整体回收过程非常慢,因此,Full GC 可能导致应用程序的暂停。

 

如上所述,只有更合理的内存分配,避免不被使用的对象频繁出现,调整堆内存的扫描时间。

 

full GC, 即全垃圾回收,是一种垃圾回收的过程 , 它会暂停所有的应用程序线程,对整个堆进行回收。 (这个太可怕了。。)

 

初始标记:首先,垃圾回收器标记出哪些对象是需要被回收的。

 

并发标记:然后,垃圾回收器将标记任务分配给多个线程,并发地执行标记任务。

 

重新标记:在并发标记的过程中,如果有新的对象被创建,需要对这些对象进行重新标记。

 

整理:接下来,垃圾回收器将没有被标记的对象整理到内存的一端。

 

回收:最后,垃圾回收器回收被标记的对象,释放内存。

 

来个图大家看的明白一些~ Full Gc 的生命流程~ 本质上就是,垃圾太多,正常的活儿干不了了,内存空间不够了,得停下所有的事情,来一次大扫除

 

 

 

二、写代码的时候能做什么?

上述可得,fullGc 是很可怕的,由于堆内存的整体回收过程非常慢,因此,Full GC 可能导致应用程序的暂停,直接就崩掉了。。。

 

要避免 Full GC 发生,本质上就需要对系统堆内存大小进行适当设置以及对代码进行优化,基本上有以下这些技巧:

 

•调整堆内存大小:确定合适的堆内存大小是避免 Full GC 发生的关键。

 

•对代码进行内存优化:使用不同的数据结构,避免内存泄漏,使用对象池等技巧。

 

•使用较大的新生代:新生代是存储短生命周期对象的内存区域,更大的新生代可以减少 Full GC 的频率。

 

•设置适当的垃圾回收算法:使用 G1 GC 算法等技术可以提高系统性能并减少 Full GC 的频率。

 

•这些是避免 Full GC 发生的一些常见建议。请注意,每种情况都不同,所以要根据具体情况选择适当的方法。

 

这些方法,看起来还是很抽象...我们来说点具体例子

 

首先,堆内存大小和垃圾回收算法,不是咱能操作和关心的,业务侧也一般不怎么会调,交给运维同学了。 浅提一下,调整内存大小:通过调整 JVM 参数,如 -Xms、-Xmx 来适当增大内存。

 

具体我们能做到的,最主要的就是减少数据对象的生命周期:

 

通过使用弱引用、软引用、虚引用等引用类型,可以在不需要数据对象时直接回收,从而避免 Full GC 。

 

减少数据对象的生命周期是指在程序中使用对象时,尽可能地缩短对象的存活时间。

 

这样可以减少垃圾对象数量,降低 Full GC 的频率。这是我们重点需要关注的!!

 

以下是一些具体的例子:

1. 避免使用不必要的临时对象:

如果程序中有大量临时对象,它们可能很快就会被垃圾回收器清理掉。因此,应该避免创建不必要的临时对象,以减少对象的生命周期。

 

eg:

 


double average(double[] values) {



在这个例子中,数组 values 是临时对象,在函数结束时会被销毁。这样,不必考虑如何删除集合,以避免内存泄漏的风险。

 

还有,

 


String concatenate(List<String> strings) {



在这个例子中,每次循环都会创建一个临时的字符串对象,并将其附加到 result 中。随着循环的进行,这些临时对象可能会堆积,导致频繁的 GC 操作。为了避免这个问题,可以使用 Java 中的 StringBuilder 来构建字符串

 


String concatenate(List<String> strings) {


这样的话,不再需要创建临时字符串对象,从而减少 GC 的次数。

2. 尽早释放对象:当对象不再需要时,应该尽早将其释放,以便及时回收它。例如,在程序完成处理后立即释放对象,而不是等到下一次需要使用它之前。

比如我们日常最常用的 for 循环就很棒,

 


for (int i = 0; i < data.length; i++) {



在这个例子中,循环变量 i 只在循环中使用,并在循环结束后释放。这样做可以减少不必要的内存使用,从而减少全垃圾回收的次数。

 

另一个具体的例子是使用 try-with-resources 语句,这可以确保流等资源在不再使用后自动关闭,例如:

 


try (FileInputStream in = new FileInputStream("file.txt")) {



在这个例子中,文件输入流在不再使用后会被自动关闭,就不用手动关,这样也会更合理~

3. 重复使用对象:如果可以,可以尝试重复使用同一个对象,而不是频繁地创建和销毁新的对象。

这个比较好理解,比如同样的事务流程,没必要搞两个变量 ~ 最少的变量干最多的活儿是最理想的~

4. 使用对象池:

可以使用对象池,重复使用固定数量的对象,而不是不断创建新的对象。这样可以减少对象的生命周期,并降低 Full GC 的频率。

 

使用对象池是一种常用的避免 Full GC 的方式。它的核心思想是重复利用已经创建好的对象,而不是每次都创建新的对象。

 

以下是一个简单的对象池的代码例子:

 


import java.util.ArrayList;



在代码中,我们创建了一个大小为 100 的对象池,并在静态代码块中初始化了 100 个对象。当我们需要使用对象时,可以调用 getObject 方法,如果对象池中有剩余的对象,就从对象池中取出一个对象;如果没有剩余对象,就新建一个对象。当不需要这个对象了,就可以调用 returnObject 方法,将对象放回对象池中。

 

这样,我们可以重复利用已经创建好的对象,减少了对象的创建和销毁的频率,从而减少了 Full GC 的几率。

5. 使用弱引用:

在程序中,如果有大量对象不会再使用,可以使用弱引用来引用它们。 这个最多应用在类型缓存这样的场景,它们不是必须的对象,因此有些时候可以直接干掉

这是一个弱引用的例子,这玩意儿还是比较抽象的。。。

 


import java.lang.ref.WeakReference;



举个更具体点的:

 


import java.lang.ref.WeakReference;



这个例子中,我们将一个 MyObject 对象封装在弱引用中,并保存在 HashMap 缓存中,当我们显式调用 System.gc()方法时,JVM 会尝试回收这些不再使用的对象,如果内存不足,则会回收 MyObject 对象,那么 cache.get("example").get()返回的将是 null。

三、测试能做啥

回顾全文,其实我们能做的真不多,只能在业务代码测试的过程中,关注对象的使用频次,拒绝无效的引用或 new 一大堆没必要的对象。

具体手段:

定期监测 GC 日志:通过我们的 jvm 关注,大项目上线后,或代码改动特别大的项目上线后,做一下读写压测的操作~ 关注我们的 jvm, jvm监测地址

 

 

 

数据结构优化: 根据上述的手段,测试开发工程师可以通过上述手段,来优化数据结构来减小数据对象的生命周期,从而避免 Full GC。在测试过程中,关注一下数据结构的合理性~

 

关注单元测试:通过运行研发的单元测试,或自己手动写一个,模拟实际的内存使用情况,来评估内存的使用情况(基本上,目前的业务代码能跑起来,大概率是没问题的,,)

总结:

日常的业务代码测试,对内存的敏感度要高一些,没 bug 不一定不会出问题,现在我们的系统是成熟的可靠的,但是面对大促的压力,如果能提前解决隐患,干掉有风险的内存使用,也是节省我们压测时的工作量嘛

标签:Full,对象,回收,FullGC,GC,内存,使用,详解
From: https://www.cnblogs.com/jiaodaoniujava/p/18046071

相关文章

  • 详解海量日志传输框架 Flume
    什么是Flume本次我们来聊一聊Flume,它是Cloudera提供的一个高可用、高可靠、分布式的日志收集框架,用于海量日志的采集、聚合以及传输。Flume在生产上使用最多的场景就是,实时读取服务器本地磁盘的数据,然后将数据写入到HDFS。Flume基础架构再来看看Flume的基础架构:Ag......
  • Java HashMap 详解
    HashMapHashMap继承自AbstractMap,实现了Map接口,基于哈希表实现,元素以键值对的方式存储,允许键和值为null。因为key不允许重复,因此只能有一个键为null。HashMap不能保证放入元素的顺序,它是无序的,和放入的顺序并不相同。HashMap是线程不安全的。1.哈希表哈希表基于数......
  • 机器学习策略篇:详解满足和优化指标(Satisficing and optimizing metrics)
    满足和优化指标要把顾及到的所有事情组合成单实数评估指标有时并不容易,在那些情况里,发现有时候设立满足和优化指标是很重要的,让我告诉是什么意思吧。假设已经决定很看重猫分类器的分类准确度,这可以是\(F_1\)分数或者用其他衡量准确度的指标。但除了准确度之外,还需要考虑运行时......
  • Java访问权限之public、protected、private详解——【包和访问控制】【访问权限控制】
    @目录包和访问控制包声明和导入访问权限控制源码:Giteehttps://gitee.com/drip123456/java-seGIthubhttps://github.com/Drip123456/JavaSE专栏:JavaSE笔记专栏包和访问控制通过前面的学习,我们知道该如何创建和使用类。包声明和导入包其实就是用来区分类位置的东西,也可......
  • Rust的Cow类型有什么用?详解Cow及其用途
    Rust的智能指针有哪些?大多数人都能马上答出Box<T>、Rc<T>和Arc<T>、Ref<T>和在异步编程中很常见的Pin<P>等等。不过,有一个可能经常被大多数人遗忘的类型,它功能强大,利用好了可以节省很多复制开销;它就是这篇文章的主角:Cow<B>。什么是COW(Copy-On-Write)?在开始之前,有必要先介绍一下CO......
  • CentOS7 安装FastDFS配置详解
    一、介绍FastDFS是一个开源的高性能分布式文件系统。它的主要功能包括:文件存储,文件同步和文件访问(文件上传和文件下载),它可以解决高容量和负载平衡问题。FastDFS应该满足基于照片共享站点和视频共享站点等文件的网站的要求。FastDFS具有两个角色:tracker和storage。tracker负责调......
  • Python面向对象,类属性,实例属性,类方法,实例方法,静态方法的区别及用法详解
    一.前言在Python的面向对象编程中,类属性和实例属性是两个不同的概念,它们在作用域和使用方式上有所区别。在Python中的面向对象编程中有三种方法:实例方法、类方法和静态方法,它们之间的差异主要体现在参数传递和调用方式上。二.面向对象-类属性和实例属性1.区别在Pyth......
  • 详解SSL证书系列(3)如何选择SSL证书
    我们知道了在网站部署SSL证书后,不管是对网站本身还是对网站的用户都能够带来许多好处。那么随着HTTPS的普及,市面上也出现了各种不同的SSL证书。并且由于SSL证书的多样性,很多人对于如何选择SSL证书有着很大的困惑。因此,本篇文章将从证书品牌,证书类型和域名类型三个方面提......
  • Lua调试函数 debug.getinfo() namewhat详解
    Lua调试的时候会用到debug.getinfo()函数,what的值文档给了解释:"Lua":Luafunction"C":Cfunction"main":mainpartofachunk(通过load函数等执行的语句)关于namewhat的值到底表示什么,官方文档只是简单列举(不全)。列举一些常见情况localgetinfo=debug.getinfol......
  • 智慧安防平台easy1400视图平台订阅功能流程详解
    在信息时代,信息的获取和传播变得至关重要。对于企业和个人而言,能够定制和接收他们感兴趣或需要的信息,将极大地提高工作效率和生活品质。本文将详细介绍如何创建和管理订阅内容,帮助您轻松掌握信息获取的主动权。 一、创建订阅首先,登录到相应的平台后,在首页界面中找到名为“下级......