消息队列高手课
李玥
京东零售技术架构部资深架构师
立即订阅
8426 人已学习
课程目录
已完结 41 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (2讲)
开篇词 | 优秀的程序员,你的技术栈中不能只有“增删改查”
免费
预习 | 怎样更好地学习这门课?
基础篇 (8讲)
01 | 为什么需要消息队列?
02 | 该如何选择消息队列?
03 | 消息模型:主题和队列有什么区别?
04 | 如何利用事务消息实现分布式事务?
05 | 如何确保消息不会丢失?
06 | 如何处理消费过程中的重复消息?
07 | 消息积压了该如何处理?
08 | 答疑解惑(一) : 网关如何接收服务端的秒杀结果?
进阶篇 (21讲)
09 | 学习开源代码该如何入手?
10 | 如何使用异步设计提升系统性能?
11 | 如何实现高性能的异步网络传输?
12 | 序列化与反序列化:如何通过网络传输结构化的数据?
13 | 传输协议:应用程序之间对话的语言
14 | 内存管理:如何避免内存溢出和频繁的垃圾回收?
加餐 | JMQ的Broker是如何异步处理消息的?
15 | Kafka如何实现高性能IO?
16 | 缓存策略:如何使用缓存来减少磁盘IO?
17 | 如何正确使用锁保护共享数据,协调异步线程?
18 | 如何用硬件同步原语(CAS)替代锁?
19 | 数据压缩:时间换空间的游戏
20 | RocketMQ Producer源码分析:消息生产的实现过程
21 | Kafka Consumer源码分析:消息消费的实现过程
22 | Kafka和RocketMQ的消息复制实现的差异点在哪?
23 | RocketMQ客户端如何在集群中找到正确的节点?
24 | Kafka的协调服务ZooKeeper:实现分布式系统的“瑞士军刀”
25 | RocketMQ与Kafka中如何实现事务?
26 | MQTT协议:如何支持海量的在线IoT设备?
27 | Pulsar的存储计算分离设计:全新的消息队列设计思路
28 | 答疑解惑(二):我的100元哪儿去了?
案例篇 (7讲)
29 | 流计算与消息(一):通过Flink理解流计算的原理
30 | 流计算与消息(二):在流计算中使用Kafka链接计算任务
31 | 动手实现一个简单的RPC框架(一):原理和程序的结构
32 | 动手实现一个简单的RPC框架(二):通信与序列化
33 | 动手实现一个简单的RPC框架(三):客户端
34 | 动手实现一个简单的RPC框架(四):服务端
35 | 答疑解惑(三):主流消息队列都是如何存储消息的?
测试篇 (2讲)
期中测试丨10个消息队列热点问题自测
免费
期末测试 | 消息队列100分试卷等你来挑战!
结束语 (1讲)
结束语 | 程序员如何构建知识体系?
消息队列高手课
登录|注册

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

李玥 2019-08-22
你好,我是李玥。今天,我们来聊一聊内存管理的问题。
不知道你有没有发现,在高并发、高吞吐量的极限情况下,简单的事情就会变得没有那么简单了。一个业务逻辑非常简单的微服务,日常情况下都能稳定运行,为什么一到大促就卡死甚至进程挂掉?再比如,一个做数据汇总的应用,按照小时、天这样的粒度进行数据汇总都没问题,到年底需要汇总全年数据的时候,没等数据汇总出来,程序就死掉了。
之所以出现这些情况,大部分的原因是,程序在设计的时候,没有针对高并发高吞吐量的情况做好内存管理。要想解决这类问题,首先你要了解内存管理机制。
现代的编程语言,像 Java、Go 语言等,采用的都是自动内存管理机制。我们在编写代码的时候,不需要显式去申请和释放内存。当我们创建一个新对象的时候,系统会自动分配一块内存用于存放新创建的对象,对象使用完毕后,系统会自动择机收回这块内存,完全不需要开发者干预。
对于开发者来说,这种自动内存管理的机制,显然是非常方便的,不仅极大降低了开发难度,提升了开发效率,更重要的是,它完美地解决了内存泄漏的问题。是不是很厉害?当年,Java 语言能够迅速普及和流行,超越 C 和 C++,自动内存管理机制是非常重要的一个因素。但是它也会带来一些问题,什么问题呢?这就要从它的实现原理中来分析。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《消息队列高手课》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(27)

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

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

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

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

    2019-08-22
    8
  • a、
    通过jstat 观察gc情况和分析gc日志,来合理分配堆内存,年轻代,年老代大小,尽量让对象在minor gc就能被回收,而不需要执行full gc。因为full gc执行速度慢,程序暂停时间就长
    2019-08-22
    5
  • godtrue
    课后思考及问题
    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
    2
    4
  • 书策稠浊
    拿到文本,异步写入硬盘,给队列一个路径,另一个线程监控队列,一个个路径拿出来加载到内存一个个处理。
    2019-08-22
    3
  • 业余草
    除了方法论,还想要一个结合方法论的demo实现!
    2019-08-22
    1
    3
  • Jxin
    需求:
    1.处理10kb的文本(文本存储和业务处理)
    2.进程不卡死(高响应服务不中断)


    分析:
    1.请求不可丢失,响应要快,允许业务处理有一定滞后性。
    2.发挥单机性能极限,但不越限导致卡顿或中断。

    方案:
    1.采用生产消费模式,接受到数据直接持久化,异步消费。为了实现数据积压,不丢失,可以走跨进程的实现,比如mq。
    2.压测文本落盘和文本业务处理两个进程的负载能力,调整其接入层线程池线程数,找到最高单机并发上限,设置令牌桶做限流。通过水平扩提高总请求负载力。落盘和业务处理两块根据并发负载力调整相对集群的节点比例。


    注:除非业务真有性能需求,不然千万别一个对象传到底。架设防腐层,业务解耦,对系统的扩展力很是重要。对于高速发展的项目(变化大且快),其价值远大于这么点性能提升。(先往易于演变的架构走,前期堆机器。真到业务量足够庞大,需要调优时再调优)
    2019-08-29
    2
  • PeterLu
    简单整理下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
    1
  • 笑傲流云
    老师,说下我的思路:1,jvm对字符串有优化,字符串是不可变对象,通过字符串常量池,可以复用一些字符串;2,文本10kb过大,是否可以拆分?建议分割文本,形成小对象直接在年轻代被垃圾回收,避免大对象直接进入老年代,引发频繁的full gc;3,Kafka底层存储机制大量使用了page cache,把子节码缓存在磁盘,避免大量对象引发gc问题。
    2019-08-23
    1
  • vi
    1. 使用对象池,重复使用对象,每次处理文件中的行数据,更新到利用的对象中
    2. 把对象外迁,使用中间件缓存对象,不被GC扫描到
    2019-08-22
    1
    1
  • 许童童
    我会考虑使用享元模式,预先分配10KB 左右的对象池,当请求进来时,从对象池中拿一个来使用,用完后,自己释放,以此来自己回收,复用这些对象,减少对象的创建,从而减少垃圾回收。
    2019-08-22
    1
  • 冰激凌的眼泪
    占用内存差不多的,是不是比较适合池化?

    作者回复: 是这样的。

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

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

    2019-08-22
    1
  • 佳佳大魔王
    创建一个静态类型的字符串来存放这个文本,这样每次改变字符串的内容就好了,而不用gc他
    2019-08-22
    4
    1
  • 努力努力再努力
    老师,假如我有一个对象,这个对象作为接口的入参,但是前端在传值的时候,只传了部分字段,那么在申请内存空间的时候,这个对象是只申请传值了的这些对象所以占用的空间,还是所有属性占用的空间

    作者回复: 这个问题比较复杂,没有统一的答案,在不同的编程语言中处理都不太一样。

    一般面向对象的语言,比如Java这种,对象的属性如果是基本类型,它的内存空间会随着对象的创建就占用上了。如果属性也是一个对象,什么时候真正去申请内存,取决于你的代码中是在什么时候new出这个属性对象的。

    2019-11-21
  • 饭粒
    看评论也学习不少东西,jvm 差不多忘光了(泪
    2019-11-05
  • 张新国
    如果是有大量的临时对象,我觉得有两种办法:1增大新生代内存占比 2是不是可以开启逃逸分析 随线程退出从栈内存中直接回收
    2019-10-30
  • 成都小郭
    disruptor,一个高性能队列,也有这样对对象的优化处理..初始化队列长度之后,就会创建那么多个对象,新消息进来就给对象赋值,减少了很多对象创建和销毁的时间
    2019-09-19
  • 布小丫学编程
    10kb还是挺大的,会扔到mq队列中,然后通过固定线程池来一一处理每个文本。整体思路还是使用异步处理的机制。
    2019-09-17
收起评论
27
返回
顶部