跑的好好的 Java 进程,怎么突然就瘫痪了
内存回收一直是 Java的痛点
用 Java 无法做出类似 Redis 这样的产品。Java 的内存回收机制使我们在编写代码时不需要关注对象的回收,同时加大了内存回收的消耗,标记复制需要做内存拷贝,标记清除算法则需要 stop the world 。所以我们在使用缓存的时候,量稍微大一些就需要借助类似 Redis 这样的中间件帮我们处理了。作为 Javaer ,我们享受了自动内存回收的安逸,同时也需要多了解下内存优化的方法。
为什么 FGC 停不下来了
什么情况下会 GC
为了了解我们的系统为什么会不停 FGC ,我们需要先了解一下系统什么情况下会 GC 。在 Jvm 层面,当我们 new 一个对象的时候, Jvm 会先在堆区分配对象需要的内存,这个时候如果内存不够的话,就需要 GC 了, GC 的返回结果就是对象的空间地址。Jvm 会先进行 ygc ,也就是我们通常说的标记复制,如果 ygc 之后依然申请不到空间,就会进行 FGC 了。同理,如果 FGC 之后依然没有足够的空间,就会循环的进行 FGC ,直到申请到足够的空间。
导致不停的 FGC 的原因
如上文所讲, FGC 有可能发生在你的每一行代码。如果 FGC 之后依然没有足够的空间,就会不停的 FGC ,直到申请到足够的空间。同时 JVM 会限制在抛出 OutOfMemory 错误之前在 GC 中花费的 VM 时间的比例。系统频繁 F 大致有五种情况:
- 内存泄漏
- 请求处理变慢导致同时申请内存的线程太多
- metaspace 耗尽
- 常量池将堆区占满
- 堆外内存耗尽
在一个高并发的系统中,多数 FGC 是请求处理变慢导致的。假设单机承受 tps 是1w,正常情况下处理一个请求的时间是 1ms ,那同一时刻并行的请求数量仅为 10 。如果性能发生抖动,每个请求处理的时间增加到 100ms ,那同一时刻并行的请求数量就会增加到 100 个。每个线程在处理请求的时候都会 new 一些对象出来,长时间存活的线程会造成类似内存泄漏的效果,将系统的内存耗尽。同时 FGC 也会加剧系统性能的开销,使系统变得更慢,产生雪崩。
如何让系统 FGC 之后仍然能活下来
杜绝内存泄漏
内存泄漏产生的原因以及解决办法网上有很多资料,这里就不写了。内存泄漏造成系统瘫痪的频率很高,有些系统定时从数据库拉取配置信息缓存到集合中,但是 set 不小心写成了 list ,最终在新增元素的时候内存溢出了。养成良好的编程习惯,多关注些细节,就能避免很多未知的问题。
并发限制:防止系统被撑死
每台服务器都有并行处理请求的上限,不管请求处理的多快,超过上限之后就会被撑死,对高并发的请求做好并发数限制是保持系统稳定的必要条件。需要注意的是,有一些系统在拒绝过多的请求时,也会做一些降级逻辑,降级逻辑也是有性能开销的,同样需要做并发限制,如果降级的请求超过并发限制,将不进行降级逻辑直接抛出异常。
自适应限流:防止系统被摸死
我们需要自适应限流有两个原因:
每台服务器所处的环境是不一样的
有些服务器和离线计算的 vm 混部在一起,有些部署在实体机,有些部署在新老型号的机器上,每台服务器能承受的 qps 并不完全一样。统一配置分布式系统中每台服务器限流阀值,要么发挥不出每台服务器应有的作用,要么在高 qps 的情况下一些比较慢的服务器宕机,所以用服务器作为限流粒度是最合适的。
设置了正确的限流阀值,也可能被摸死
当单机承受的 QPS 6~20 倍于限流的流量时,拒绝一次请求的开销就无法忽略不记了。譬如春晚活动有些系统设置了正确的限流也被 6~20 倍于限流的流量冲垮。这种死法称为被摸死。应对这种情况,我们可以做的是在受到 6~20 倍的大流量时,动态减少限流的阀值。比如系统最开始接受 1000qps ,5000 的拒绝流量过来会把系统摸死,这个时候我们调整系统的阀值,限流设置到 100 ,被摸死的阀值就可以高一些,这样就算有 6000 个请求进来,我们系统也可以保证活下来。
阿里有结合算法动态调整单机限流阀的产品,已经对外公布了,感兴趣的同学可以搜一下淘系技术公众号中的 诺亚自适应限流 的相关内容。
异常流量监控:防止长尾请求拖垮系统
我们盯系统监控的时候通常会关注 99 分位的数据,但如果设置了合理的限流,系统依然被流量打挂,就要从那百分之一的长尾数据入手了。有些长尾数据对系统的影响会非常大。想象如果一个 put 请求传过来几十兆的数据,对 Java 是极为不友好的,很有可能产生 FGC ,让请求变慢,导致一系列问题。
总之,磨刀不误砍柴工,当我们的系统因为 FGC 一次又一次重启的时候,不如花时间了解下系统产生性能问题的原因,将产生问题的那根针拔掉,晚上睡个安稳觉,白天更加充满活力的挖新坑。希望每个程序员手里都是一个稳定的系统。
作者信息:通木, Github 账号 zhdd99 ,阿里巴巴基础设施事业部高级开发工程师,目前主要负责阿里巴巴IDC监控系统。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
拼不过 GO?阿里如何重塑云上的 Java
阿里妹导读:Java 诞生于20年前,拥有大量优秀的企业级框架,践行 OOP 理念,更多体现的是严谨以及在长时间运行条件下的稳定性和高性能。反观如今,在要求快速迭代交付的云场景下,语言的简单性似乎成了首要的要求,而传统的 Java 语言显得有一些过于重量了。今天,阿里 JVM 团队技术专家郁磊(花名:梁希)分享 JVM 团队是如何面对和处理集团巨大的业务规模和复杂的业务场景的。 音乐无国界,但是音乐人有国界。 云原生亦如此。虽没有限定的编程语言,但应用所使用的编程语言已经决定了应用部署运行的行为。 ElasticHeap Java 常因为耗资源而受诟病,其中最显著一点就是 Heap 对内存的占用,即便没有请求在处理也没有对象分配,进程仍然会保留完整的堆内存空间,保障 GC 进行分配内存和操作内存的快速敏捷。 AJDK ZenGC/ElasticHeap 双十一全面支持核心链路上百应用和数十万实例。 JDK12 开始支持固定时间的触发 concurrent mark 并在 remark 中收缩 Java 堆归还内存的功能,然而并未解决在 stw 中增加暂停时间的问题,因此无法在每次 yo...
- 下一篇
getty 发布,一个完全基于 java 实现的 aio 框架
说说写这个框架的原因: 1、作者本人是一个码农,比较喜欢研究技术,特别是网络通讯。 2、JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO 2.0。正式提供了异步文件I/O操作,同时提供了与UNIX网络编程事件驱动I/O对应的AIO。AIO的发布使得实现一套网络通讯框架变得相对简单。但如果你不努力,可能也无法理解哦。 3、本人对netty比较喜欢,无论是其性能还是编程思想(JBOSS提供的一个java开源网络框架,可以说是java网络通讯里的一哥,极其稳定和强大的性能使得被广泛使用) 4、有了netty为何还要自己造轮子?这里有两个原因,其一是本人就喜欢造轮子,这是病,改不了。其二,netty经过多年的发展,其生态体系已经比较庞大,导致其代码比较臃肿,再者其高深的设计哲学我等凡夫俗子很难悟其精髓。因而索性自己造一个。 5、netty毕竟是别人的东西,还是老外的。并且国内也有许多优秀的开源框架。想了想,为何不自己搞一个呢,于是乎脑袋发热,抽时间造了一个。 说说getty的特点: 1、完全基于java aio,整个工程只依赖 slf4j(一个日志的门面框架),对工程几乎没有入侵性...
相关文章
文章评论
共有0条评论来说两句吧...