19-案例实战剖析-日处理上亿数据的系统内存分析和优化

简介: 这是当时开发中遇到的一个真实场景,也是大部分人在开发项目中有可能会遇到的一些场景,该系统主要是做大数据相关计算分析的,日处理数据量在上亿的规模。这里我们重点针对JVM内存的管理来进行模型分析,数据的来源获取主要是MYSQL数据库以及其他数据源里提取大量的数据,通过加载到JVM内存的过程我们来一起分析出现的问题以及如何优化解决

案例实战剖析-日处理上亿数据的系统内存分析和优化

1.系统背景

这是当时开发中遇到的一个真实场景,也是大部分人在开发项目中有可能会遇到的一些场景,该系统主要是做大数据相关计算分析的,日处理数据量在上亿的规模。这里我们重点针对JVM内存的管理来进行模型分析,数据的来源获取主要是MYSQL数据库以及其他数据源里提取大量的数据,通过加载到JVM内存的过程我们来一起分析出现的问题以及如何优化解决(如下图所示):

2.生产环境

这是一套分布式运行系统,生产环境部署了多台服务器(每台4核8G配置),每台机器大概每分钟负责执行100次数据提取和计算,每次提取大概1万条左右的数据到内存计算,平均每次计算需要耗费10秒左右时间。 JVM内存总共分配了4G,堆内存占3G,其中新生代和老年代分别是1.5G的内存空间

3.过程分析

按照上述的背景和实际生产环境,那每次1万条数据会占用多少的内存空间呢?这里每条数据较大,平均包含20个字段,可以认为每条数据大概在1KB左右。那么1万条数据对应就是10MB大小。那么运行多久就会导致新生代塞满呢?

新生代总共分配1.5G,那么Eden区分配就是1.2G,S1和S2区分别是150MB;如下图:

现在我们可以来手动计算下了,1次往Eden区里填充10MB对象,1分钟读取100次,也就是差不多1个G,那也就是1分钟左右的时候我们的Eden区就差不多填满了,这个时候如果触发Minor GC,我们通过上文学习知道,JVM在执行Minor GC之前是会进行一步检查动作的:老年代可用内存空间是否大于新生代全部对象?如果是第一次运行到这儿,那么我们的老年代是空的,也就是有1.5G的空间,完全是够用的。

这里触发Minor GC进行回收,但是问题在于如何回收呢?我们重点来看每次任务计算的耗时是10S,这里差不多有80次的任务都已经执行完毕了,还有大概20个任务正在计算中,也就是对应还有200MB的对象在引用着,这部分对象是不会被回收的,而我们的幸存者区域最大也就是150MB无法存放下200MB,那么根据我们讲过的空间担保机制,这200MB对象会直接进入到老年代!

由于每一分钟就会将Eden区填满触发Minor GC,也就是每分钟就会有200MB对象进入到老年代,那当老年代的内存占用的越多后会发生什么事情呢?比如两分钟过去了,这时占用400MB,那老年代可用空间就只剩1.1G了,那第三分钟触发Minor GC的时候,一判断发现,老年代剩余空间已小于Eden区所有对象1.2G大小了,则会走另一条分支的判断了,我们可以根据下图再来回顾下:

先看参数: -XX:-HandlePromotionFailure是否设置,当然一般都会设置,此时会判断老年代连续空间是否大于历史平均晋升老年代对象的大小,那历史晋升对象大小都在200MB,很明显大于,那么JVM会直接进行冒险操作,触发Minor GC的执行,而本次冒险是成功的!新生代依然继续晋升200MB对象到老年代。

那么当系统运行到第7分钟的时候,这时进入到老年代的对象有1.4G了,剩余空间仅剩100MB!如下图:

系统运行到这儿,发现老年代剩余空间已经比历史平均晋升对象大小都要小了,这时会直接触发Full GC!假设老年代空间都可以被回收,那么这时老年代对象就完全清除,接着会继续进行Minor GC,200MB对象继续进入老年代,又开始重复循环执行了。

那么按照以上的运行分析,我们可以得出一个结论就是:系统平均运行7、8分钟左右就会触发一次Full GC的执行!而每次一旦Full GC执行,就会严重影响到系统的运行效率,加上该系统的Full GC频率较高,给用户带来的使用感受是非常糟糕的!

4.JVM优化

像真实开发中大家也有很大几率会遇到类似这样的情况,我们应该减少Full GC的次数以及降低它出现的频率,甚至不触发Full GC,那么如何进行优化呢?这也是考验一个Java程序员的价值体现。

针对类似的计算系统,每次Minor GC的时候,必然会有一部分数据没处理完毕,但是按照现有的内存模型,我们的幸存者区域只有150MB是无法满足200MB对象的存放,因此有必要调整我们的内存比例。

解决方案:

3GB的堆内存大小,我们直接分配2G给新生代,1G给老年代,这样Survivor区的大小就有200MB了每次刚好能存放下MinorGC过后存活的对象了。如下图所示:

只要每次Minor GC时200MB存活对象可以存放进Survivor区,那么等下一次Minor GC时这部分对象对应的计算任务也已经结束,也可以直接进行回收。

那么接下来我们还是在继续模拟跑一次,当Eden区内存已经装满,此时S0区也有200MB对象,这是触发Minor GC的执行,200MB正在执行的任务对象(存活对象)直接转移到S1区,回收清空掉Eden区和S0区,如下图:

那么通过以上的分析也不免看出,基本上很少会有对象进入到老年代,我们也成功的将几分钟一次的Full GC降低到几个小时一次,大幅度提升了系统的性能,避免了Full GC对系统运行的影响!

当然这里其实还有一个细节点:就是动态年龄对象规则!如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到-XX: MaxTenuringThreshold中要求的年龄。 这里需要结合自己公司的实际系统分析到底有多少对象是根据动态年龄规则进入到了老年代,如果要避免因为这项规则进入老年代,从而触发Full GC也可以尝试调整Eden区和Survivor区的比例,调整survivor区的大小。

目录
相关文章
|
17天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
4天前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
24 11
|
13天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
35 6
|
11天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
13天前
|
监控 安全 程序员
如何使用内存池池来优化应用程序性能
如何使用内存池池来优化应用程序性能
|
13天前
|
存储 监控 Java
深入理解计算机内存管理:优化策略与实践
深入理解计算机内存管理:优化策略与实践
|
21天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
138 9
|
25天前
|
存储 JavaScript 前端开发
如何优化代码以避免闭包引起的内存泄露
本文介绍了闭包引起内存泄露的原因,并提供了几种优化代码的策略,帮助开发者有效避免内存泄露问题,提升应用性能。
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
378 0
|
26天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
55 1
下一篇
无影云桌面