如何避免内存溢出和频繁的垃圾回收

简介: 代码明明简单,日常跑没问题,怎么一大促就卡死甚至进程挂掉?大多因为设计时,就没针对高并发、高吞吐量case考虑过内存管理。

0 OOM和频繁GC预防方案

代码明明简单,日常跑没问题,怎么一大促就卡死甚至进程挂掉?大多因为设计时,就没针对高并发、高吞吐量case考虑过内存管理。


1 自动内存管理机制的实现原理

内存管理主要考虑:


1.1 申请内存

计算要创建对象所需要占用的内存大小

在内存中找一块儿连续且空闲内存空间,标记为已占用

把申请的内存地址绑定到对象的引用上,这时候对象就能使用

1.2 内存回收

1.2.1 找出所有可回收对象,将对应内存标记为空闲

找出可回收对象,现代GC算法大多采用“标记-清除”算法或变种:


标记阶段:从GC Root开始,可简单将GC Root理解为程序入口的那个对象,标记所有可达对象,因为程序中所有在用的对象一定都会被这个GC Root对象直接或间接引用

清除阶段:遍历所有对象,找出所有没标记的对象。这些没有标记的对象可被回收,清除这些对象,释放对应内存

该算法最大问题:在执行标记和清除过程中,须STW,否则计算结果不准确,所以程序会卡死。后续产生许多变种算法,但都只能减少一些进程暂停的间,不能完全避免STW。


1.2.2 整理内存碎片

完成对象回收后,还需要整理内存碎片。


所以,GC完成后,还需内存碎片整理,将不连续空闲内存移到一起,以空出足够连续内存空间。内存碎片整理也有很多实现,但由于整理过程中需移动内存数据,也须STW。


虽然自动内存管理机制有效解决内存泄漏问题,代价是执行垃圾回收时会STW,若暂停时间过长,程序就“卡死了”。


STW发生在标记阶段 or 清除阶段?

标记阶段需要暂停,清除阶段一般不需要。进程暂停这个实现过程是怎样的?暂停后需要再启动,这个又是一个怎样的过程?


https://stackoverflow.com/questions/16558746/what-mechanism-jvm-use-to-block-threads-during-stop-the-world-pause


STW为使计算结果更准确,好比打扫卫生,我一个房间一个房间来,也不耽误其他房间的事,是不是暂停是不必须的,其实 young gc 几乎不停发生,只有发生full gc 的时候性能才会大大降低?


对GC来说只有一个房间,你是没有办法分成多个完全独立的小房间。 像java中的young gc就是为缓解这个问题,而变种算法可减少Full GC次数,但没办法完全避免FullGC。


内存清除这个动作具体是怎么实现的?

内存是按页为单位管理,即一块块,JVM有一套复杂数据结构来记录它管理的所有页面与对象引用之间的关系。所谓清除和移动对象,就是修改这个记录关系的数据结构。


2 高并发下程序为何卡死

微服务收到一个请求后,执行一段业务逻辑,返回响应。这过程中,会创建一些对象,如请求对象、响应对象和处理中间业务逻辑的对象。随该请求响应的处理流程结束,创建的这些对象也都没用,将在下次GC时被释放。直到下次GC前,这些无用对象还会一直占用内存。


低并发时,单位时间需处理请求不多,创建对象数量也不多,自动GC机制发挥很好,它能选择在系统不太忙时执行GC,每次GC的对象也不多,因此STW时间很短,短到人类无法感知。


高并发时程序忙,短时内创建大量对象,迅速占满内存,这时无内存可用,GC开始启动,并且这次被迫执行的GC面临的是占满整个内存的海量对象,其执行时间也长,相应回收过程会导致进程长时间暂停,进一步导致大量请求被积压待处理。等GC刚结束,更多请求立刻涌进,迅速占满内存,再次被迫执行GC,进入恶性循环。若GC速度跟不上创建对象速度,还可能OOM。


3 高并发环境的内存管理

GC不可控,无法避免。但可降低GC频率,减少进程暂停时长。只有使用过被丢弃的对象才是GC目标,想办法在处理大量请求同时,尽量少的产生这种一次性对象:


最有效的,优化代码处理请求的业务逻辑,尽量少创建一次性对象,尤其大对象。如把收到请求的Request对象在业务流程中一直传递下去,而非每执行一个步骤,就创建一个和Request对象差不多的新对象。


需频繁使用,占用内存较大的一次性对象,可考虑自行回收并复用。为这些对象建立一个对象池。收到请求后,在对象池内申请一个对象,使用完后再放回对象池,就能复用对象,有效避免频繁GC


使用更大内存的服务器。


根本办法:绕开自动GC机制,自己实现内存管理。但自行管理内存带来很多问题,极大增加程序复杂度,可能引起内存泄漏等。Flink就自行实现一套内存管理机制,一定程度缓解了处理大量数据时GC问题,但总体效果并非很好。


FAQ

微服务需求是处理大量文本,如每次请求会传入10KB文本,高并发时,如何优化程序,尽量STW?


这种一般不要求时延,大部分异步处理,更注重服务吞吐率,服务可在更大内存服务器部署,然后把新生代eden设置更大,因为这些文本处理完不会再拿来复用,朝生夕灭,可在新生代Minor GC,防止对象晋升到老年代,防止频繁Major GC.


若晋升对象过多,大于老年代的连续内存空间,也会触发Full Gc,然后在这些处理文本的业务流程中,防止频繁创建一次性的大对象,把文本对象做为业务流程直接传递下去,若这些文本需复用,可将他保存起来,防止频繁创建。


JVM对字符串有优化,字符串是不可变对象,通过字符串常量池,可以复用一些字符串。


文章知识点与官方知识档案匹配,可进一步学习相关知识

————————————————

目录
相关文章
|
3月前
|
监控 Java
压力测试Jmeter的简单使用,性能监控-堆内存与垃圾回收 -jvisualvm的使用
这篇文章介绍了如何使用JMeter进行压力测试,包括测试前的配置、测试执行和结果查看。同时,还探讨了性能监控工具jconsole和jvisualvm的使用,特别是jvisualvm,它可以监控内存泄露、跟踪垃圾回收、执行时内存和CPU分析以及线程分析等,文章还提供了使用这些工具的详细步骤和说明。
压力测试Jmeter的简单使用,性能监控-堆内存与垃圾回收 -jvisualvm的使用
|
14天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
36 6
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
66 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 监控 算法
Java中的内存管理与垃圾回收机制解析
本文深入探讨了Java编程语言中的内存管理方式,特别是垃圾回收机制。我们将了解Java的自动内存管理是如何工作的,它如何帮助开发者避免常见的内存泄漏问题。通过分析不同垃圾回收算法(如标记-清除、复制和标记-整理)以及JVM如何选择合适的垃圾回收策略,本文旨在帮助Java开发者更好地理解和优化应用程序的性能。
|
1月前
|
监控 算法 Java
Java中的内存管理:理解垃圾回收机制
【10月更文挑战第2天】 在本文中,我们将深入探讨Java编程语言中的内存管理机制,特别是垃圾回收机制。我们将从基本原理、垃圾回收算法到实际应用场景全面解析,帮助你更好地理解和优化Java应用的内存使用。无论你是初学者还是有经验的开发者,这篇文章都能带给你新的启发和思考。
32 2
|
2月前
|
存储 算法 Java
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
115 0
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
|
1月前
|
存储 监控 算法
深入理解Java内存模型与垃圾回收机制
【10月更文挑战第10天】深入理解Java内存模型与垃圾回收机制
20 0
|
2月前
|
监控 算法 Java
Java中的内存管理与垃圾回收机制
本文将深入探讨Java编程语言中的内存管理方式,特别是垃圾回收(Garbage Collection, GC)机制。我们将了解Java虚拟机(JVM)如何自动管理内存,包括对象创建、内存分配以及不使用对象的回收过程。同时,我们还将讨论不同的垃圾回收算法及其在不同场景下的应用。
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
2月前
|
监控 算法 Java
Java中的内存管理:理解垃圾回收机制的深度剖析
在Java编程语言中,内存管理是一个核心概念。本文将深入探讨Java的垃圾回收(GC)机制,解析其工作原理、重要性以及优化方法。通过本文,您不仅会了解到基础的GC知识,还将掌握如何在实际开发中高效利用这一机制。