会员登录 - 用户注册 - 设为首页 - 加入收藏 - 网站地图 完蛋!我被 Out of Memory 包围了!!

完蛋!我被 Out of Memory 包围了!

时间:2025-05-20 11:22:53 来源:锐评时讯 作者:咨询 阅读:878次

先点赞再看,养成好习惯。

是极致魅惑、洒脱自在的。 Java。heap sp。ac。e。

是知性柔情、温婉大气的。 GC overhe。ad。limit exceeded。

是纯真无邪、活泼可爱的。 Metaspace。

假如以上不是你的菜,那还有……。

刁蛮固执,无迹可寻的。 CodeCache。

性感火辣、心思细腻的。 Direct Mem。or。y。

尊贵冷傲,独爱你一人的。 OOM Killer。

总有一款,能让你钟情!BUG 挑选权,现在交由你手!

Java heap space。

这是最常见的一个 OOM 问题了,谁还没经历过一个 Heap OOM呢?

当堆内存被塞满之后,一边 GC 无法及时收回,一边又在继续创立新目标,Allocator 无法分配新的内存之后,就会送一个 OOM 的过错:

java.lang.OutOfMemoryError: Java heap space。

剖析处理起来无非是那几步:

dump 堆内存。

经过 MAT、YourKit、JProfiler 、IDEA Profiler 等一系列东西剖析dump文件。

找到占用内存最多、最大的目标,看看是哪个小可爱干的。

剖析代码,测验优化代码、削减目标创立。

添加 JVM 堆内存、约束恳求数、线程数、添加节点数量等。

常见类库运用误区。

尤其是一些东西库,尽或许的避免每次新建目标,然后节约内存提高功用。

大多数干流的类库,进口类都确保了单例线程安全,大局保护一份即可。

举一些常见的过错运用比方:

Apache HttpClient。

CloseableHttpClient ,这玩意相当于一个“浏览器进程”了,背面有衔接池衔接复用,一堆机制的辅佐类,假如每次都 new 一个,不只速度慢,并且浪费了许多资源。

比较正常的做法是,大局保护一个(或许依据事务场景分组,每组一个)实例,服务启动时创立,服务封闭时毁掉:

CloseableHttpClient httpClient = HttpClients.custom()                .setMaxConnPerRou。te。(maxConnPerRoute)                .setMaxConnTotal(maxConnTotal)                /// ...                                 .build();

Gson。

究竟是 Google 的项目,进口类天然也是完成了线程安全,大局保护一份 Gson 实例即可。

Jackson。

Jackson 作为 Spring MVC 默许的 JSON 处理库,功用强大、用户许多,xml/json/yaml/properties/csv 各种干流格局都支撑,单例线程安全天然也是 ok 的,大局保护一份 ObjectMapper 即可。

GC overhead limit exceeded。

这个过错比较有意思,上面的 Java heap space 是内存完全满了之后,还在继续的创立新目标,此刻服务会完全假死,无法处理新的恳求。

而这个过错,仅仅表明 GC 开支过大,Collector 花了许多的时刻收回内存,但开释的堆内存却很小,并不代表服务死了。

此刻程序处于一种很奇妙的状况:堆内存满了(或许到达收回阈值),不断的触发 GC 收回,但大多数目标都是可达的无法收回,一起 Mutator 还在低频率的创立新目标。

呈现这个过错,一般都是流量较低的场景,有太多常驻的可达目标无法收回,但是吧,GC 后闲暇的内存还能够满意服务的根本运用。

不过此刻,已经在频频的老时代GC了,老时代又大目标又多、在现有的收回。算法。下,GC 功率十分低并切资源占用巨大,甚至会呈现把。 CPU。打满的状况。

呈现这个过错的时分,从监控视点看起来或许是这个姿态:

恳求量或许并不大。

不断 GC,并切暂停时刻很长。

时不时的还有新的恳求,但呼应时刻很高。

CPU 利用率很高。

究竟仍是堆内存的问题,排查思路和上面的。 Java heap space。没什么差异。

Metaspace/PermGen。

Metaspace 区域里,最首要的便是 Class 的元数据了,ClassLoader 加在的数据,都会存储在这里。

MetaSpace 初始值很小,默许是没有上限的。当利用率超越40%(默许值 MinMetaspaceFreeRatio)会进行扩容,每次扩容一点点,扩容也不会直接 FullGC。

比较。引荐。的做法,是不给初始值,但约束最大值:

-XX:MaxMetaspaceSize=。

不过仍是得当心,这玩意满了成果很严重,轻则 Full GC,重则 OOM:

java.lang.OutOfMemoryError: Metaspace。

排查 MetaSpace 的问题,首要思路仍是追寻 Class Load数据,比较干流的做法是:

经过 Arthas 之类的东西,检查 ClassLoader、lo。adC。lassess 的数据,剖析数量较多的 ClassLoader 或许 Class。

打印每个 class 的加载日志:-XX:+TraceClassLo。adi。ng -XX:+TraceClassUnloading。

下面介绍几个常见的,或许导致 MetaSpace 增加的场景:

反射运用不当。

JAVA 里的反射,功用是十分低的,以反射的目标必须得缓存起来。尤其是这个。Method。目标,假如在并发的场景下,每次都获取新的 Method,然后 invoke 的话,用不了多久 MetaSpace 就给你打爆!

简略的说,并发场景下,Method.invoke 会重复的动态创立 class,然后导致 MetaSpace 区域增加,具体剖析能够参阅笨神的文章《从一起GC血案谈到反射原理》。

用反射时,尽或许的用老练的东西类,Spring的、Apache的都能够。它们都内置了reflec。ti。on相关目标的缓存,功用又全功用又好,足以处理日常的运用需求。

一些 Agent 的 bug。

一些 Java Agent,静态的和运行时注入的都算。根据 Instrumentation 这套 A。PI。做了各种增强,一会 load 一会 redefine 一会remove的,假如不当心呈现 BUG,也很简单生成许多动态的 class,然后导致 metaspace 打满。

动态署理问题。

像 Spring 的 AOP ,也是根据动态署理完成的,不管是 CgLib 仍是 JDK Proxy,不管是 ASM 仍是 ByteBuddy。终究的成果都逃不开动态创立、加载 Class,有这两个操作,那 Metaspace 必定受影响。

Spring 的 Bean 默许是。 singleton。的,假如装备为。 prototype。,那么每次 getBean 就会创立新的署理目标,从头生成动态的 class、从头 define,MetaSpace 天然越来越大。

Code Cache。

Code Cache 区域,存储的是 JIT 编译后的热门代码缓存(留意,编译过程中运用的内存不归于 Code cache),也归于 non heap 。

假如 Code cache 满了,你或许会看到这么一条日志:

Server VM warning: CodeCache is full. Compiler has been disabled.。

此刻 JVM 会禁用 JIT 编译,你的服务也会开端变慢。

Code Cache 的上限默许比较低,一般是240MB/128MB,不同渠道或许有所差异。

能够经过参数来调整 Code Cache 的上限:

-XX:ReservedCodeCacheSize=。

只需尽量避免过大的Class、Method ,一般也不太会呈现这个区域被打满的问题,默许的 240MB/128MB 也足够了。

Direct Memory。

Direct Memory 区域,一般称之为直接内存,许多涉及到 磁盘I/O ,Socket I/O 的场景,为了“Zero Copy”提高功用都会运用 Direct Memory。

就比方 Netty ,它真的是把 Direct Memory 玩出了花(有空写一篇 Netty 内存办理剖析)……。

运用 Direct Memory时,相当于直接绕过 JVM 内存办理,调用 malloc() 函数,体会手动办理内存的趣味~。

不过吧,这玩意运用比较风险,一般都合作 Unsafe 操作,一个不当心地址读写的地址过错,就能得到一个 JVM 给你的惊喜:

## A fatal error has been detected by the Java Runtime Environment:##  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffdbd5d19b4, pid=1208, tid=0x0000000000002ee0## JRE version: Java(TM) SE Runtime Environment (8.0_301-b09) (build 1.8.0_301-b09)# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.301-b09 mixed mode windows-。amd。64 compressed oops)  # Problemat。ic。frame:# C  [msvcr100.dll+0x119b4]# # No core dump will be written. Minidu。mps。are not enabled by default on client versions of Windows## If you would like to。 sub。mit a bug report, please visit:#   http://bugreport.java.com/bugreport/crash.jsp# The crash happened outside the Java Virtual Machine in native code.# See problematic frame for where to report the bug.#。

更多的解说,能够参阅我这篇《Java中的Heap Buffer与Direct Buffer》。

这个 Direct Memory 区域,默许是无上限的,但为了避免被 OS Kill,仍是会约束一下,给个256MB或许更小的值,避免内存无限增加:

-XX:MaxDirectMemorySize=。

假如 Direct Memory 到达 MaxDirectMemorySize 并且无法开释时,就会得到一个 OOM过错:

java.lang.OutOfMemoryError: Direct buffer memory。

Linux。OOM Killer。

跳出 JVM 内存办理之后,当 OS 内存耗尽时,Linux 会挑选内存占用最多,优先级最低或许最不重要的进程杀死。

一般在容器里,首要的进程便是肯定是咱们的 JVM ,一旦内存满,第一个杀的便是它,并且仍是 kill -TERM (-9)。信号。,打你一个猝不及防。

假如 JVM 内存。参数。装备合理,远低于容器内存约束,仍是呈现了 OOM Killer 的话,那么祝贺你,大概率是有什么 Native 内存走漏。

这部分内存,JVM 它还管不了。

除了 JVM 内部的 Native 走漏 BUG 这种小概率事件外,大概率是你引证的第三方库导致的。

这类问题排查起来十分费事,究竟在 JVM 之外,只能靠一些原生的东西去剖析。

并且吧,这种动不动就要 root 权限的东西,但是得领导批阅请求权限的……排查本钱真的很高。

排查 Native 内存的根本的思路是:

pmap 检查内存地址映射,定位可疑内存块、剖析内存块数据。

strace 手动追寻进程体系调用,剖析内存分配的体系调用链路。

替换jemalloc/tcmalloc之类的内存分配器(或许 async-profiler有个支撑native 剖析的分支)追寻malloc的调用链路。

现在最常见的 Native 内存走漏场景,是 JDK 的 Inflater/Deflater 这俩卧龙凤雏,功用是供给 GZIP 的紧缩、解压,在默许 glibc 的 malloc 完成下,很简单呈现“内存走漏”。假如呈现 Native 内存走漏,能够先看看使用里有没有 GZIP 相关操作,说不定有惊喜。


好了,各类风格的 OOM 都感触完了,究竟哪一个更能感动你呢?

审阅修改 黄宇。

内容来源:https://bachduy.com/app-1/highlight argentina,http://chatbotjud.saude.mg.gov.br/app-1/868bet.com

(责任编辑:社会)

    系统发生错误

    系统发生错误

    您可以选择 [ 重试 ] [ 返回 ] 或者 [ 回到首页 ]

    [ 错误信息 ]

    页面发生异常错误,系统设置开启调试模式后,刷新本页查看具体错误!