当前位置: 首页 > news >正文

项目报 OutOfMemoryError 、GC overhead limit exceeded 问题排查以及解决思路实战

项目报 OutOfMemoryError、GC overhead limit exceeded 问题排查以及解决思路实战

前言:
问题现象描述:
1,生产环境有个定时任务,没有初始化告警数据【告警数据量为1000多个】
2,其他定时任务执行正常
3,查询日志到定时任务执行之前有日志打印
4,手动触发补偿告警定时任务接口报OutOfMemoryError GC overhead limit exceeded,也会报

1、现象问题排查

1.1 程序是否触发判断

1,首先定时任务之前可以正常初始化告警指标数据,说明程序可以正常执行,不会存在问题【初次判断】

1.2 JVM内存大小查看

2,会不会是内存不够用导致的结果,使用arthas工具查看内存使用情况
输入memory 返回如下信息
在这里插入图片描述

1,其中堆空间eden_space 区内存:总共462M,已使用310M
2,还剩下大概 150M左右【大概够用,只是猜想】

1.3 手动触发定时任务查看内存使用情况

1,现象是eden_space 很快达到99%,并且报GC overhead limit exceeded
在这里插入图片描述

1.4 查看定时任务代码逻辑,发现创建大量对象大概1000多个对象【在一瞬间】,代码大概如下。

1,为啥是1000多个对象,是因为有1000多个告警,要在凌晨触发定时任务生成告警指标数据
2,告警的数据,是kafka接收的,kafka监听到数据实时拉取数据,批量保存到数据库
3,告警有4大类,每个类有12个类别

未优化前逻辑

private final int batchSize = 500;
public void initKafkaAlarmInventoryTask() {// 待批量保存清单数据List<TaskAlarmInventoryEntity> batchInsertTaskInventoryList = new ArrayList<>();// 1.获取需要初始化的告警数据 // TODO: 从数据库查询 List<InitAlarmTaskInventoryEntity> initTaskInventoryEntities = .....//  initInspectionSystemMap key:为告警的类型【12大类型】 v:每个类型下面的指标集合initInspectionSystemMap.forEach((k, v) -> {// 对每个指标进行遍历v.forEach(x -> {// 相关业务逻辑 如果当前的告警数据已经消费到,就跳过,否则就保存消息batchInsertTaskInventoryList.add(....);}});});if (CollectionUtils.isNotEmpty(batchInsertTaskInventoryList)) {for (int i = 0; i < batchInsertTaskInventoryList.size(); i += batchSize) {int endIndex = Math.min(i + batchSize, batchInsertTaskInventoryList.size());// TODO:保存到数据库}}}}

优化后:

private final int batchSize = 500;
public void initKafkaAlarmInventoryTask() {// 待批量保存清单数据List<TaskAlarmInventoryEntity> batchInsertTaskInventoryList = new ArrayList<>();// 1.获取需要初始化的告警数据 // TODO: 从数据库查询 List<InitAlarmTaskInventoryEntity> initTaskInventoryEntities = .....//  initInspectionSystemMap key:为告警的类型【12大类型】 v:每个类型下面的指标集合initInspectionSystemMap.forEach((k, v) -> {// 对每个指标进行遍历v.forEach(x -> {// 相关业务逻辑 如果当前的告警数据已经消费到,就跳过,否则就保存消息batchInsertTaskInventoryList.add(....);}});if (CollectionUtils.isNotEmpty(batchInsertTaskInventoryList)) {for (int i = 0; i < batchInsertTaskInventoryList.size(); i += batchSize) {int endIndex = Math.min(i + batchSize, batchInsertTaskInventoryList.size());// TODO:保存到数据库}}}});}

4,可能是一次性初始化1000多个对象把堆内存使用完导致的这个问题,优化成根据告警指标类型分类 分成几百个类初始化,打包重新调用后,还是同样问题报错【本地执行没有问题】

1.5 使用arthas 跟踪接口执行情况

使用trace命令查看每个方法的调用时间,以及调用情况

[arthas@13362]$ trace com.xxx.xx.xx.kafka.KafkaAlarmReportConsumerClient initKafkaAlarmInventoryTask

命令大概意思是:
arthas 允许你监控指定类的方法执行过程,记录每个方法执行的时间、调用链等详细信息。
com.xxx.xx.xx.kafka.KafkaAlarmReportConsumerClient:这是目标类的完全限定名,表示 KafkaAlarmReportConsumerClient类。
initKafkaAlarmInventoryTask:这是你要追踪的方法名。Arthas 将监控该方法的执行,并返回详细的执行信息。

返回如下消息:

`---[18808.94593ms] com.xx.xxx.platform.kafka.KafkaAlarmReportConsumerClient $$EnhancerBySpringCGLIB$\$4ead41fc:initKafkaAlarmInventoryTask() [throws Exception]+---[100.00% 18808.534277ms ] org.springframework.cglib.proxy.MethodInterceptor:intercept() [throws Exception]|   `---[99.93% 18794.767357ms ] com.xx.xxx.xx.kafka.KafkaReportConsumerClient:initKafkaAlarmTask() [throws Exception]|       +---[0.00% 0.032337ms ] com.xx.xx.platform.entity.dao.TaskInventoryDao:builder() #826|       +---[0.00% 0.220948ms ] com.xxx.major.common.utils.DateUtils:getStartDate() #826|       +---[0.00% 0.014243ms ] com.xx.xx.platform.xxx.xx.TaskInventoryDao$TaskInventoryDaoBuilder:synStartDate() #826|       +---[0.00% 0.224577ms ] com.xxx.xxx.common.utils.DateUtils:getEndDate() #826|       +---[0.00% 0.010579ms ] com.xxx.major.xxx.entity.xxx.TaskInventoryDao$TaskInventoryDaoBuilder:synEndDate() #826|       +---[0.00% 0.009368ms ] com.xx.major.xxx.entity.xxx.TaskInventoryDao$TaskInventoryDaoBuilder:build() #826|       +---[0.21% 38.643543ms ] com.xxx.major.xxx.service.TaskInventoryService:queryTaskInventoryByTaskInventoryDao() #825|       +---[0.60% 112.059449ms ] com.xxx.major.platform.service.InitTaskInventoryService:queryAllInitTaskInventory() #836|       +---[0.00% 0.286756ms ] com.xxx.major.common.utils.DateUtils:getEndDate() #842|       +---[0.17% 32.261925ms ] com.xxxx.major.platform.service.TaskInventoryService:queryTaskInventoryByParam() #842`---throw:java.lang.OutOfMemoryError #-1 [GC overhead limit exceeded]

返回的大概意思是:

2.3.1. 方法调用路径:
com.xxx.major.platform.kafka.KafkaAlarmReportConsumerClient KaTeX parse error: Can't use function '$' in math mode at position 22: …erBySpringCGLIB$̲\$4ead41fc:init…EnhancerBySpringCGLIB$$4ead41fc)是由 Spring 的 CGLIB 动态代理生成的类,initKafkaAlarmTask 是该方法。它的执行时间是 18808.94593ms(约 18.8 秒)。

org.springframework.cglib.proxy.MethodInterceptor:intercept() [throws Exception]:
这表示 Spring AOP 拦截器的执行,它拦截了对 initKafkaAlarmTask 的调用,并花费了 18808.534277ms。

com.xxx.major.platform.kafka.KafkaReportConsumerClient:initKafkaAlarmTask() [throws Exception]:
这表示实际的业务逻辑方法 initKafkaAlarmTask 被执行,并且占用了 18794.767357ms,几乎占用了整个时间。

2.3.2. 方法调用过程中的内部调用:
接下来是对 initKafkaAlarmTask 方法内部的其他方法的详细追踪:

TaskAlarmInventoryDao.builder():用时 0.032337ms。
DateUtils.getStartDate():用时 0.220948ms。
TaskAlarmInventoryDao T a s k A l a r m I n v e n t o r y D a o . s y n S t a r t D a t e ( ) :用时 0.014243 m s 。 D a t e U t i l s . g e t E n d D a t e ( ) :用时 0.224577 m s 。 T a s k A l a r m I n v e n t o r y D a o TaskAlarmInventoryDao.synStartDate():用时 0.014243ms。 DateUtils.getEndDate():用时 0.224577ms。 TaskAlarmInventoryDao TaskAlarmInventoryDao.synStartDate():用时0.014243msDateUtils.getEndDate():用时0.224577msTaskAlarmInventoryDaoTaskAlarmInventoryDao.synEndDate():用时 0.010579ms。
TaskAlarmInventoryDao$TaskAlarmInventoryDao.build():用时 0.009368ms。
TaskAlarmInventoryService.queryTaskIAlarmnventoryByTaskInventoryDao():用时 38.643543ms。
InitTaskAlarmInventoryService.queryAllAlarmInitTaskInventory():用时 112.059449ms。
TaskAlarmInventoryService.queryTaskAlarmInventoryByParam():用时 32.261925ms。

简短说,异常信息是:
最后,输出中显示了一个 java.lang.OutOfMemoryError 异常,指示发生了 GC overhead limit exceeded 错误。这表示在执行过程中,JVM 因垃圾回收器(GC)花费了过多的时间,但并未有效释放内存,导致内存溢出异常。

throw:java.lang.OutOfMemoryError #-1 [GC overhead limit exceeded] 表明 JVM 由于 GC 限制,无法回收足够的内存,最终触发了 OutOfMemoryError。

1.6 查看JVM参数配置:

arthas 输入 jvm,返回如下内容 生成环境是1g,测试环境是512M这里只是做个已有JVM参数样例
在这里插入图片描述
1,问题一:
永久代和 Metaspace 配置:

由于 Java 8 及以后版本中已经没有永久代(PermGen),而是使用 Metaspace,因此 -XX:PermSize 和 -XX:MaxPermSize 参数已经不再生效。
建议: 移除这些不再有效的参数,使用 Metaspace 相关的参数,如 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize,例如

-XX:MetaspaceSize=128M
-XX:MaxMetaspaceSize=512M

2,问题二:
当发生 OutOfMemoryError(OOM)错误时,JVM(Java虚拟机)会生成一个堆转储文件(heap dump)。这个文件包含了JVM内存堆的快照,包含了Java对象的详细信息,帮助开发者和运维人员分析和定位内存泄漏或内存不足的原因。分析堆转储文件是解决这类问题的一个重要步骤。

  1. 堆转储文件的生成
    当出现 OutOfMemoryError 时,JVM会通过以下方式生成堆转储文件:

使用 -XX:+HeapDumpOnOutOfMemoryError 参数自动在OOM发生时生成堆转储。
堆转储文件通常会保存到指定的文件路径,如:-XX:HeapDumpPath=/path/to/dump.hprof。

查看当前文件夹存在xxx.hprof快照信息

在这里插入图片描述

1.7 将堆转储文件copy出来,用jdk自带的 java VisualVm分析

1,双击执行

在这里插入图片描述

2,文件 装入 快照信息

在这里插入图片描述

3,文件类型选择并装入

在这里插入图片描述

4,注意文件类型,是否选错
5,点击异常线程选择,找到问题所在

在这里插入图片描述

6,问题定位

找到问题的代码位置
在这里插入图片描述

7,根据问题找到的代码位置

在这里插入图片描述

8,计算类所占内存的大小

这里可以看到最大的有char[]实例大小和总大小,总实例的个数是740890,总大小是306,889,752单位是B,换成MB为,306,889,752➗1024➗1024≈292.67287445068359375≈293MB,这个跟启动JVM参数设置最大堆内存设置-Xmx512M 和这个相差不大**(-Xmx=512m虽然堆分配是512m,但是JVM会拓展)**在这里插入图片描述
在这里插入图片描述
如果没有显示引用下面的数据,记得把这个点开
在这里插入图片描述

2、问题跟踪解决

问题一:
1,定时任务触发,会很一次性触发1000多个对象,
解决方案:
拆分成根据告警指标类型进行分开,仍然是批处理,一次性创建的对象最大为500个左右
问题二:
根据上面排查原因点7,发现是kafka消费者拉取数据量为500而且是多线程导致,造成数据量庞大,内存OOM,
解决方案:
1、把多线程去掉,或者最大线程数改为单个,每次拉取500个数据,
2、查看当前服务器内存使用情况
free -h
返回如下,mem的free还剩1.2G,加到当前服务JVM参数即可
在这里插入图片描述
并调整-Xms堆的大小 改为512m+1024m

3、遇到的问题

1,arthas 服务器拉取不到,需要外网下载好arthas,copy到服务器
电脑盘创建文件夹执行如下命令即可
参考官网网址:https://arthas.aliyun.com/doc/install-detail.html

-- 下载arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
-- 执行脚本
java -jar arthas-boot.jar

2,服务器hprof快照信息,导出win环境,可能权限不对,要将hprof快照信息设置成所有人都可执行的权限

-- 修改为所有用户都可以执行
chmod a+rwx java_pid25304.hprof

在这里插入图片描述

3,JVM已有参数配置失效,JVM调优参数下一期讲解 待完成…

本次OOM,GC问题做个记录分享给大家,生产项目难免有些没有表达合理CV清楚,欢迎指出来,我这边改好更新后再发上去
喜欢我的文章记得点个在看,或者点赞,持续更新中ing…

相关文章:

项目报 OutOfMemoryError 、GC overhead limit exceeded 问题排查以及解决思路实战

项目报 OutOfMemoryError、GC overhead limit exceeded 问题排查以及解决思路实战 前言&#xff1a; 问题现象描述&#xff1a; 1&#xff0c;生产环境有个定时任务&#xff0c;没有初始化告警数据【告警数据量为1000多个】 2&#xff0c;其他定时任务执行正常 3&#xff0c;查…...

【计算机-显示屏灰阶测试】

硬计算机-显示屏灰阶测试 ■ 对比度■ 清晰度■ 灰度色阶&#xff08;色带&#xff09;■ 对比率■■ ■ 对比度 在一个性能良好的显示器上&#xff0c;您可观察到每种颜色的标尺都可分为从 1 至 32、大致上等宽但不同亮度的色带。即使是在刻度1处的色带也应该隐约可见。 一个…...

CSS系列(40)-- Container Queries详解

前端技术探索系列&#xff1a;CSS Container Queries详解 &#x1f4e6; 致读者&#xff1a;探索组件响应式的艺术 &#x1f44b; 前端开发者们&#xff0c; 今天我们将深入探讨 CSS Container Queries&#xff0c;这个强大的组件级响应式特性。 基础概念 &#x1f680; 容…...

工作生活做事慢效率低原因及解决方案

时间和效率管理具体版&#xff08;初阶&#xff09;&#xff08;一&#xff09; 工作&生活做事慢效率低原因及解决方案 一、效率慢的原因&#xff08;动物解析法&#xff08;编者自创&#xff09;&#xff09; 打败你的可能是生活的小事 1.无头苍蝇无流程 做事之前没有想…...

各种数据库类型介绍

在软件开发和数据处理领域&#xff0c;数据库扮演着至关重要的角色。它们用于存储、检索和管理大量数据&#xff0c;是信息系统不可或缺的基础。以下是几种常用的数据库类型及其简要介绍&#xff1a; 1.关系型数据库&#xff08;Relational Databases&#xff09; 关系型数据库…...

了解智能运维

智能运维 &#xff08;一&#xff09;运维工作的转变 随着技术发展&#xff0c;运维工作从基础的搬机器、插网线、装系统等体力活儿&#xff0c;逐渐转变为更侧重服务器管理、代码管理、日志分析、监控告警、流量管理及故障排查等的脑力劳动。如今&#xff0c;运维人员拿到的…...

js实现仿windows文件名称排序

引言&#xff1a; 在JavaScript中&#xff0c;数组排序是一个常见的操作&#xff0c;但默认的Array.sort()方法只能进行简单的字符串比较。在处理复杂数据时&#xff0c;我们需要自定义排序函数来满足特定的需求。本文将通过一个具体的代码示例&#xff0c;解释如何实现一个仿w…...

基于Oauth2的SSO单点登录---前端

Vue-element-admin 是一个基于 Vue.js 和 Element UI 的后台管理系统框架&#xff0c;提供了丰富的组件和功能&#xff0c;可以帮助开发者快速搭建现代化的后台管理系统。 一、基本知识 &#xff08;一&#xff09;Vue-element-admin 的主要文件和目录 vue-element-admin/ |--…...

springboot 使用注解设置缓存时效

springboot 使用注解设置缓存时效 import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.cache.RedisCache; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCach…...

QGIS二次开发(地图符号库操作)

实习三 地图符号库操作 3.1 任务要求 基于QGIS&#xff0c;实现地图符号的设计/存储与显示&#xff1b;基于QGIS实现一个点、线、面shp矢量图层文件的显示。通过设置引用的符号&#xff0c;改变矢量图层的显示效果&#xff1b;可编辑地图的符号库汇中的点符号、线符号、面符号…...

线性代数行列式

目录 二阶与三阶行列式 二元线性方程组与二阶行列式 三阶行列式 全排列和对换 排列及其逆序数 对换 n阶行列式的定义 行列式的性质 二阶与三阶行列式 二元线性方程组与二阶行列式 若是采用消元法解x1、x2的话则得到以下式子 有二阶行列式的规律可得&#xff1a;分…...

Vision Transformer (ViT) 论文的第二句话

Vision Transformer (ViT) 论文的第二句话 flyfish 原句&#xff1a; “In vision, attention is either applied in conjunction with convolutional networks, or used to replace certain components of convolutional networks while keeping their overall structure in…...

Github 2024-12-27 Java开源项目日报Top10

根据Github Trendings的统计,今日(2024-12-27统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目9Kotlin项目1C#项目1非开发语言项目1C++项目1《Hello 算法》:动画图解、一键运行的数据结构与算法教程 创建周期:476 天协议类型:Ot…...

气相色谱-质谱联用分析方法中的常用部件,分流平板更换

分流平板&#xff0c;是气相色谱-质谱联用分析方法中的一个常用部件&#xff0c;它可以实现气相色谱柱流与MS检测器流的分离和分流。常见的气质联用仪分流平板有很多种&#xff0c;如单层T型分流平板、双层T型分流平板、螺旋分流平板等等。 操作视频http://www.spcctech.com/v…...

centos7 免安装mysql5.7及配置(支持多个mysql)

一&#xff09; 下载免安装包&#xff1a; mysql下载地址: https://dev.mysql.com/downloads/mysql/下载时&#xff0c;选择以前5.7版本&#xff1a; image 下载第一个TAR压缩包&#xff1a; image 二&#xff09; 定义安装路径并解压安装包 1、假设需要把MySQL放到 /usr/local…...

Python的Pandas--Series的创建和实现

1.Series函数的格式&#xff1a; pandas.Series(data,index,dtype,name,copy) data&#xff1a;一组数据&#xff08;ndarray类型、list、dict等类&#xff09;或标量值 index&#xff1a;数据索引标签。如果不指定&#xff0c;默认为整数&#xff0c;从0开始 dtype&#x…...

OCR实践-问卷表格统计

前言 书接上文 OCR实践—PaddleOCROCR实践-Table-Transformer 本项目代码已开源 放在 Github上&#xff0c;欢迎参考使用&#xff0c;Star https://github.com/caibucai22/TableAnalysisTool 主要功能说明&#xff1a;对手动拍照的问卷图片进行统计分数&#xff08;对应分数…...

uniapp中的条件编译

在script中 // #ifdef APP-PLUS console.log("11"); // #endif// #ifdef MP-WEIXIN console.log("22"); // #endif 在template中 <!-- #ifdef APP-PLUS --><view>哈哈哈</view> <!-- #endif --><!-- #ifdef MP-WEIXIN -->…...

Segment Routing Overview

大家觉得有意义和帮助记得及时关注和点赞!!! Segment Routing (SR) 是近年来网络领域的一项新技术&#xff0c;“segment” 在这里 指代网络隔离技术&#xff0c;例如 MPLS。如果快速回顾网络设计在过去几十年的 发展&#xff0c;我们会发现 SR 也许是正在形成的第三代网络设计…...

【K8s】专题十五(6):Kubernetes 网络之 Pod 网络调试

本文内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01; 如果对您有帮助&#xff0c;烦请点赞、关注、转发、订阅专栏&#xff01; 专栏订阅入口 | 精选文章 | Kubernetes | Docker | Linux | 羊毛资源 | 工具推荐 | 往期精彩文章 【Docker】&#xff08;全…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

rknn toolkit2搭建和推理

安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 &#xff0c;不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源&#xff08;最常用&#xff09; conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...