消息队列高手课
李玥
美团高级技术专家
52199 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 42 讲
进阶篇 (21讲)
消息队列高手课
15
15
1.0x
00:00/00:00
登录|注册

14 | 内存管理:如何避免内存溢出和频繁的垃圾回收?

内存碎片整理
标记-清除算法
使用更大内存的服务器
对象池
优化业务逻辑
内存回收的过程
申请内存的逻辑
高并发下的内存管理技巧
自动内存管理机制的实现原理
思考题
为什么在高并发下程序会卡死?
内存管理

该思维导图由 AI 生成,仅供参考

你好,我是李玥。今天,我们来聊一聊内存管理的问题。
不知道你有没有发现,在高并发、高吞吐量的极限情况下,简单的事情就会变得没有那么简单了。一个业务逻辑非常简单的微服务,日常情况下都能稳定运行,为什么一到大促就卡死甚至进程挂掉?再比如,一个做数据汇总的应用,按照小时、天这样的粒度进行数据汇总都没问题,到年底需要汇总全年数据的时候,没等数据汇总出来,程序就死掉了。
之所以出现这些情况,大部分的原因是,程序在设计的时候,没有针对高并发高吞吐量的情况做好内存管理。要想解决这类问题,首先你要了解内存管理机制。
现代的编程语言,像 Java、Go 语言等,采用的都是自动内存管理机制。我们在编写代码的时候,不需要显式去申请和释放内存。当我们创建一个新对象的时候,系统会自动分配一块内存用于存放新创建的对象,对象使用完毕后,系统会自动择机收回这块内存,完全不需要开发者干预。
对于开发者来说,这种自动内存管理的机制,显然是非常方便的,不仅极大降低了开发难度,提升了开发效率,更重要的是,它完美地解决了内存泄漏的问题。是不是很厉害?当年,Java 语言能够迅速普及和流行,超越 C 和 C++,自动内存管理机制是非常重要的一个因素。但是它也会带来一些问题,什么问题呢?这就要从它的实现原理中来分析。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

在高并发场景下,内存管理成为软件开发中的一个关键问题。本文介绍了自动内存管理机制的实现原理,以及在高并发情况下可能出现的内存溢出和频繁垃圾回收的问题。现代编程语言采用自动内存管理机制,简化了开发者的工作,但在执行垃圾回收时会导致进程暂停,尤其在高并发情况下容易触发进程暂停。文章通过分析内存管理的基本原理和高并发场景下的内存管理问题,帮助读者了解了内存管理的重要性和挑战。 在高并发情况下,垃圾回收是不可避免的,但可以通过优化代码逻辑来减少一次性对象的产生,从而降低垃圾回收的频率和进程暂停的时长。另外,建立对象池来回收和重用占用内存较大的一次性对象,以及使用更大内存的服务器,都可以有效缓解垃圾回收带来的进程暂停问题。然而,要根本解决这一问题,需要绕开自动垃圾回收机制,自行实现内存管理,但这会带来程序复杂度增加和可能的内存泄漏等问题。 总的来说,现代编程语言采用自动内存管理机制,但在高并发场景下会面临内存管理的挑战。通过优化代码逻辑、建立对象池、使用更大内存的服务器等方法,可以在一定程度上缓解垃圾回收导致的进程暂停问题。然而,要根据具体应用情况综合权衡,选择相对最优的解决方案。 文章提出了一个思考题,即如何在高并发情况下处理大量文本以尽量避免垃圾回收导致的进程卡死问题。这个问题值得读者深入思考和讨论。 通过本文,读者可以了解到在高并发场景下内存管理的重要性,以及如何通过优化和选择合适的解决方案来缓解垃圾回收带来的问题。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《消息队列高手课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(41)

  • 最新
  • 精选
  • linqw
    尝试回答下课后习题,老师有空帮忙看下哦 如果有一个微服务是处理大量的文本,感觉这种一般不会要求时延,大部分都会进行异步处理,更加注重服务的吞吐率,服务可以在更大的内存服务器进行部署,然后把新生代的eden设置的更大些,因为这些文本处理完不会再拿来复用,朝生夕灭,可以在新生代Minor GC,防止对象晋升到老年代,防止频繁的Major GC,如果晋升的对象过多大于老年代的连续内存空间也会有触发Full Gc,然后在这些处理文本的业务流程中,防止频繁的创建一次性的大对象,把文本对象做为业务流程直接传递下去,如果这些文本需要复用可以将他保存起来,防止频繁的创建。也为了保证服务的高可用,也需对服务做限流、负载、兜底的一些策略。

    作者回复: 思路非常清晰,赞👍

    2019-08-22
    111
  • leslie
    一路跟着老师学到现在我大致明白了老师想阐述什么或者说上次回答我的困惑时的答案了;其实老师是想传授:为何要用消息队列、如何使用、何种场景下使用其涉及什么知识我们应当如何把握它的使用。 老师上次的回答提到程序不用太深:不过其实程序、网络还有今天的课程提及的内存管理-其实是计算机组成原理的东西,如何合理的去结合这些知识才是消息队列把握好的关键;就像老师今天留的题目其实就是需要程序的垃圾回收机制的知识和组成原理的内存管理的知识结合才能给出相应的正确答案,不知道是否可以这样理解老师今天的题目? 同时在跟几位老师的课一起学习知识并梳理自己从业多年的知识体系:至少让我觉得之前对于课程的选择是正确的,至少从大的方面去理解了;老师其实是在授之与渔,而非简单的授之与鱼。 期待老师的下节课:希望老师解答一下我对于问题方向上的理解是否正确,谢谢。

    作者回复: 我希望更给大家的,既能有鱼,先填饱肚子解决手上的问题,然后还能有渔,学到捕鱼的技能,受用终生。

    2019-08-22
    36
  • 课后思考及问题 1:这个算法有一个最大问题就是,在执行标记和清除过程中,必须把进程暂停,否则计算的结果就是不准确的。这也就是为什么发生垃圾回收的时候,我们的程序会卡死的原因。后续产生了许多变种的算法,这些算法更加复杂,可以减少一些进程暂停的时间,但都不能完全避免暂停进程。 对于这段有几个问题? 1-1:进程必须暂停,是在标记阶段还是在清除阶段?还是两者都会? 1-2:进程暂停这个实现过程是怎样的?暂停后需要再启动,这个又是一个怎样的过程? 1-3:后面解释进程必须暂停的原因是为了使计算结果更加准确,我觉得好比打扫卫生,我一个房间一个房间来,也不耽误其他房间的事,是不是暂停是不必须的,其实 young gc 几乎不停的在发生,只有发生full gc 的时候性能才会大大降低? 1-4:内存清除这个动作具体是怎么实现的?是电平复位?还是打上可以继续使用的标位?如果打标位这个该怎么打呢?一位一位的打?还是一个字节一个字节的打?更或者是一块一块的打?

    作者回复: A1: 标记阶段需要暂停,清除阶段一般是不需要的。 A2:这个问题有点复杂,你可以参考一下:https://stackoverflow.com/questions/16558746/what-mechanism-jvm-use-to-block-threads-during-stop-the-world-pause A3:对于GC来说只有一个房间,你是没有办法分成多个完全独立的小房间的。 像java中的young gc就是为了缓解这个问题,而产生的变种算法,它可以减少FullGC的次数,但没有办法完全避免FullGC。 A4:内存是按页为单位管理的,也就是一块一块的,对于JVM来说,它有一套复杂的数据结构来记录它管理的所有页面与对象引用之间的关系。所谓清除和移动对象,就是修改这个记录关系的数据结构。

    2019-08-23
    3
    19
  • lupguo
    高并发和避免gc,尽可能少的系统调用次数,让用户态的应用程序可以快速接受tcp传过来的数据(增大接收套接字的buffer缓冲区大小,可以降低用户态和内核态的内存拷贝频次,降低上下文切换开销)。 全业务处理流程考虑用指针传递,避免内存拷贝或者堆上内存开销(栈上开销os自行回收),降低被gc可回收的变量基数。 考虑业务处理线程或协程去复用一些申请的内存区域,比如go中的buffer pool,以及通过buffer reset在处理完业务时候自动释放,可以复用申请的内存区域。 通过pprof,runtime去监控和观察内存和、gc的实际情况做对照,了解应用程序实际内存的使用情况。 暂时想到这么多。

    作者回复: 👍👍👍

    2020-05-09
    13
  • 努力努力再努力
    老师,假如我有一个对象,这个对象作为接口的入参,但是前端在传值的时候,只传了部分字段,那么在申请内存空间的时候,这个对象是只申请传值了的这些对象所以占用的空间,还是所有属性占用的空间

    作者回复: 这个问题比较复杂,没有统一的答案,在不同的编程语言中处理都不太一样。 一般面向对象的语言,比如Java这种,对象的属性如果是基本类型,它的内存空间会随着对象的创建就占用上了。如果属性也是一个对象,什么时候真正去申请内存,取决于你的代码中是在什么时候new出这个属性对象的。

    2019-11-21
    4
  • 冰激凌的眼泪
    占用内存差不多的,是不是比较适合池化?

    作者回复: 是这样的。

    2019-08-22
    3
  • 木小柒
    接上一条,点击空白留言出去了,这个功能好尴尬。可以先看机器能给到的内存量和cpu消耗,看大约一秒钟可以处理多少文件。然后限流,可以把文件存本地,也可以存消息队列中,看资源来定。控制文件数量,虽然处理排队慢了,但不至于挂掉。

    作者回复: 那能否用本节课中学到的一些内存管理的方法来解决呢?

    2019-08-22
    1
  • Hurt
    其他语言的内存管理 也存在类似的现象吗 也是这么处理吗 老师

    作者回复: 是的,要解决的问题是一样的,解决的思路也是差不多的,具体的实现上会有很多细节上的不同。

    2019-08-22
    1
  • grey927
    对于需要频繁使用,占用内存较大的一次性对象,我们可以考虑自行回收并重用这些对象。实现的方法是这样的:我们可以为这些对象建立一个对象池。 ---》这个是不是就是设计模式中的享元模式的实际应用

    作者回复: 它们之间的思想是类似的。

    2020-05-09
  • Peter
    简单整理下jvm的一些概念,帮大家回忆回忆这些理论哈哈 垃圾回收算法: 标记清除:效率较低,会产生内存碎片 复制算法:将内存一分为二,通过不断将活着的对象移动到内存另一面,再清除这面,解决了效率低、内存碎片的问题,引来新的问题:内存一分为二代价太高 标记-整理算法:先标记(过程跟标记清除一样)再将存活对象都向一端移动,清理掉端边界以外的内存。适用于老年代 分代收集算法:将内存划分为几块,新生代采用复制算法,老年代采用标记-整理算法 垃圾收集器: Serial收集器:新生代采用复制算法,会stop the world;老年代采用标记-整理算法,也会stop the world ParNew收集器:Serial收集器的多线程版本,其他一模一样 Parallel Scavenge收集器:特点:可控制的吞吐量 CMS收集器:特点:重视服务响应速度,降低GC停顿时间 大致分为4个步骤 初始标记 并发标记 重新标记 并发清除 会在初始标记和重新标记这两步stop the world G1收集器:特点:可预测的停顿,可以明确指定在一个长度为M毫秒的时间片段内,消耗在GC上的时间不得超过N毫秒 G1的运作大致分为以下几步: 初始标记 并发标记 最终标记 筛选回收 会在初始标记、最终标记、筛选回收时stop the world
    2019-10-28
    37
收起评论
显示
设置
留言
41
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部