作者:京东科技 白洋

前言:

背景:
为应对618、双11大促,消费金融侧会根据零售侧大促节奏进行整体系统备战。对核心流量入口承载的系统进行加固优化,排除系统风险,保证大促期间系统稳定。
由于大促期间,消费金融业务承担着直面用户,高并发,系统风险大概率直接造成资损风险等问题。 在日常压测和大促期间,经常会发生Jvm出现大量young Gc 和 部分full GC的情况,导致性能下降,可用率降低等情况。 之前对Jvm的垃圾回收机制不是很熟,如何避免和如何调优,基本上一窍不通,本文也是对自己学到的知识的一个巩固~

一、什么是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) {
double sum = 0;
for (double value : values) {
sum += value;
}
return sum / values.length;
}

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

还有,

String concatenate(List<String> strings) {
String result = "";
for (String str : strings) {
result += str;
}
return result;
}

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

String concatenate(List<String> strings) {
StringBuilder result = new StringBuilder();
for (String str : strings) {
result.append(str);
}
return result.toString();
}

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

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

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

for (int i = 0; i < data.length; i++) {
// do something with data[i]
}

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

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

try (FileInputStream in = new FileInputStream("file.txt")) {
// use the input stream
} catch (IOException e) {
// handle exception
}

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

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

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

4. 使用对象池:

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

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

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

import java.util.ArrayList;
import java.util.List; public class ObjectPool {
private static final int POOL_SIZE = 100;
private static final List<Object> pool = new ArrayList<>(POOL_SIZE);
static {
for (int i = 0; i < POOL_SIZE; i++) {
pool.add(new Object());
}
}
public static Object getObject() {
if (pool.isEmpty()) {
return new Object();
}
return pool.remove(0);
}
public static void returnObject(Object object) {
pool.add(object);
}
}

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

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

5. 使用弱引用:

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

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

import java.lang.ref.WeakReference;

public class WeakReferenceExample {
public static void main(String[] args) {
Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<>(obj); obj = null;
System.gc();
if (weakRef.get() != null) {
System.out.println("Object is still alive");
} else {
System.out.println("Object has been garbage collected");
}
}
}

举个更具体点的:

import java.lang.ref.WeakReference;
import java.util.HashMap; public class WeakReferenceExample {
public static void main(String[] args) {
HashMap<String, WeakReference<MyObject>> cache = new HashMap<>();
MyObject obj = new MyObject("example");
cache.put("example", new WeakReference<>(obj));
obj = null;
System.gc();
System.out.println(cache.get("example").get());
} static class MyObject {
String name; public MyObject(String name) {
this.name = name;
} @Override
public String toString() {
return "MyObject{" + "name='" + name + ''' + '}';
}
}
}

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

三、测试能做啥

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

具体手段

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

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

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

总结:

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

浅析大促备战过程中出现的fullGc,我们能做什么?的更多相关文章

  1. iOS 自己主动登录,登录过程中一直显示载入页

    iOS开发中 假设client做的人性化一点肯定会考虑自己主动登录 事实上原理非常easy,就是再首次登录成功之后将username和password存入userdefault 下次登录的时候推断us ...

  2. ubuntu安装过程中遇到的一些问题及解决办法。

    由于ubuntu与win10的双系统安装过程百度一下就有很多,在此不再赘述. (其实主要是忘记拍照片了,我一个菜鸡说得肯定也没有那些大佬们好,但网上确实也很多哈,加上有中文引导安装,问题不大.) 此篇 ...

  3. 一个请求中,ADF、JSF究竟做了哪些工作

    在Oracle ADF开发中,一个请求发生后,经过ADF处理后,我们可以很快得到响应页面,但在请求过程中ADF框架在背后究竟做了什么东西呢?今天让我们一起来了解下,ADF.JSF是基于组件模型的,不同 ...

  4. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果

    目前Android的实现是:有来电时,音乐声音直接停止,铃声直接直接使用设置的铃声音量进行铃声播放. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果. 如果 ...

  5. 记一次删除Git记录中的大文件的过程

    app/test/target/ #查看大文件 git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/ ...

  6. 重学 Java 设计模式:实战责任链模式「模拟618电商大促期间,项目上线流程多级负责人审批场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 场地和场景的重要性 射击

  7. 电商大促准备流程v2

    1 概述 对于电商企业而言,每年都会有几次大的促销活动,像双十一.店庆等,对于第一次参加这个活动的新手,难免会有些没有头绪,因而将自己参加双十一.双十二活动中的过程心得进行下总结,一方面供以后工作中继 ...

  8. 【TOP100案例专访】当当网工程师林嘉琦谈双11大促经验及APM实践

    导读:第七届TOP100全球软件案例研究峰会将于11月30日-12月3日在北京国家会议中心举办,本届峰会以“释放AI生产力 让组织向智能化演进”为开幕式主题,旨在推动企业在趋势下拥抱AI.探索和思考A ...

  9. 浅析I/O处理过程与存储性能的关系

    浅析I/O处理过程与存储性能的关系 https://community.emc.com/docs/DOC-28653 性能”这个词可以说伴随着整个IT行业的发展,每次新的技术出现,从硬件到软件大多数情 ...

  10. 大促密集,CDN如何保障电商体验如丝般顺滑?

    简介: 前不久,阿里云技术天团空降CSDN在线峰会,对核心技术竞争力进行解读.其中,阿里云高级技术专家曾福华分享了<双11: CDN如何保障电商大促如丝般顺滑>的议题.俗话说:养兵千日,用 ...

随机推荐

  1. 解读分布式调度平台Airflow在华为云MRS中的实践

    摘要:Airflow是一个使用Python语言编写的分布式调度平台,通过DAG(Directed acyclic graph 有向无环图)来管理任务. 本文分享自华为云社区<分布式调度平台Air ...

  2. 总结MySQL 的一些知识点:MySQL 连接的使用

    MySQL 连接的使用 在前几章节中,我们已经学会了如何在一张表中读取数据,这是相对简单的,但是在真正的应用中经常需要从多个数据表中读取数据. 本章节我们将向大家介绍如何使用 MySQL 的 JOIN ...

  3. [BitSail] Connector开发详解系列四:Sink、Writer

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 Sink Connector BitSail Sink Connector交互流程介绍 Sink:数据写入组件的生 ...

  4. 开发调试更便捷!火山引擎 DataLeap 提供 Notebook 交互式开发体验

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 Notebook 是一种支持 REPL 模式的开发环境. 所谓「REPL」,即「读取-求值-输出」循环:输入一段代 ...

  5. MyBatis 核心组件 —— Configuration

    概述 Mybatis 的核心组件如下所示: Configuration:用于描述 MyBatis 的主配置信息,其他组件需要获取配置信息时,直接通过 Configuration 对象获取.除此之外,M ...

  6. DS | 折半查找二叉判定树的画法

    以下给出我在学习中总结的一种比较简便的 构造折半二叉判定树 的思路以及方法: 思路分析: 在计算 \(mid\) 值时,使用的时 \(mid=(low+high)/2\) .这里由于 \(mid\) ...

  7. 如虎添翼!高德地图+Serverless 护航你的假日出行

    ​ 作者 | 刘金龙(福辰) 高德团队 引言 ​ "前方事故多发地段,请注意保持车距..." "您已疲劳驾驶,请注意休息..." "前方经过泰山旅游景 ...

  8. 2023年AI领域行业洞察,看这30个统计数据就够了!

    PrimiHub一款由密码学专家团队打造的开源隐私计算平台,专注于分享数据安全.密码学.联邦学习.同态加密等隐私计算领域的技术和内容. 随着AIGC的爆火,企业越来越多地开始采用生成式人工智能.自然语 ...

  9. BOM概述

  10. VuePress搭建博客部署Gitee Pages

    使用技术 VuePress + vuepress-theme-reco VuePress简介 VuePress文档地址 简洁至上:以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作. ...