Java 业务开发常见错误 100 例
朱晔
贝壳金服资深架构师
52944 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 48 讲
代码篇 (23讲)
Java 业务开发常见错误 100 例
15
15
1.0x
00:00/00:00
登录|注册

17 | 别以为“自动挡”就不可能出现OOM

根据实际需求合理设置参数
请求使用过多内存
使用WeakReference包装Value
Value持有Key的强引用导致无法回收
使用HashSet去重
大量相同用户名索引导致缓存的用户信息过多
直接内存OOM
元空间OOM
栈OOM
堆内存的OOM
避免Metaspace区的OOM
Groovy动态语言实现
Spring的ConcurrentReferenceHashMap
解决方案
问题分析
配置max-http-header-size参数过大
解决方案
问题分析
弱引用和垃圾回收
解决方案
问题分析
使用HashMap存放用户数据
实现自动完成功能
缓存全量用户数据
Java程序的常见OOM类型
内存空间有限
思考与讨论
Tomcat参数配置不合理导致OOM
使用WeakHashMap不等于不会OOM
太多份相同的对象导致OOM
Java自动垃圾收集器
别以为“自动挡”就不可能出现OOM

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

你好,我是朱晔。今天,我要和你分享的主题是,别以为“自动挡”就不可能出现 OOM。
这里的“自动挡”,是我对 Java 自动垃圾收集器的戏称。的确,经过这么多年的发展,Java 的垃圾收集器已经非常成熟了。有了自动垃圾收集器,绝大多数情况下我们写程序时可以专注于业务逻辑,无需过多考虑对象的分配和释放,一般也不会出现 OOM。
但,内存空间始终是有限的,Java 的几大内存区域始终都有 OOM 的可能。相应地,Java 程序的常见 OOM 类型,可以分为堆内存的 OOM、栈 OOM、元空间 OOM、直接内存 OOM 等。几乎每一种 OOM 都可以使用几行代码模拟,市面上也有很多资料在堆、元空间、直接内存中分配超大对象或是无限分配对象,尝试创建无限个线程或是进行方法无限递归调用来模拟。
但值得注意的是,我们的业务代码并不会这么干。所以今天,我会从内存分配意识的角度通过一些案例,展示业务代码中可能导致 OOM 的一些坑。这些坑,或是因为我们意识不到对象的分配,或是因为不合理的资源使用,或是没有控制缓存的数据量等。
第 3 讲介绍线程时,我们已经看到了两种 OOM 的情况,一是因为使用无界队列导致的堆 OOM,二是因为使用没有最大线程数量限制的线程池导致无限创建线程的 OOM。接下来,我们再一起看看,在写业务代码的过程中,还有哪些意识上的疏忽可能会导致 OOM。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

文章总结:本文通过案例展示了Java中WeakHashMap可能导致OOM的问题,并提供了解决方案和替代方案。作者以用户数据缓存为例,说明了使用WeakHashMap时可能出现的内存泄漏问题,指出WeakHashMap的Value持有Key中对象的强引用导致Key无法回收,建议使用WeakReference包装Value来解决。另外,文章还介绍了Spring提供的ConcurrentReferenceHashMap,该类不仅性能更好,还可以确保线程安全。此外,文章还分享了Tomcat参数配置不合理导致OOM的案例,强调了参数配置需根据实际需求来修改,避免占用不必要的资源。总结指出,读者在使用WeakHashMap时需注意内存泄漏问题,同时提供了解决方案和替代方案。文章通过具体案例和源码分析,深入浅出地解释了WeakHashMap可能出现的OOM问题,对读者编写业务代码时避免出现OOM问题具有一定的借鉴意义。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 业务开发常见错误 100 例》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(15)

  • 最新
  • 精选
  • 一个汉子~
    针对第二点,可以先compile,然后在内存中保存,脚本内容的hash作为key,compile结果作为value,用ConcurrentReferenceHashMap保存 同样的风险还出现在表达式框架aviator中

    作者回复: 👍🏻

    2020-04-18
    3
    27
  • Darren
    试着回到下问题: 第一个: 肯定是软引用,因为弱引用是只要GC执行,扫描到就被回收,缓存的作用是为了提高速度,要有一定的存在周期;如果是弱引用,每次GC执行,缓存被回收,缓存命中率超低,完全达不到缓存的作用,而又要维护缓存和DB的数据一致性问题,得不偿失。 第二个: 第一种不完美的方案:GroovyShell不要设置成全局的,每次运行时,都创建一个GroovyShell,然后限制元数据区大小,当元数据回收时,GroovyShell和该GroovyShell加载的类一起被回收; 第二种方法:GroovyShell设置为全局的,然后使用缓存,每次都是先从缓存中获取,获取不到了,在加载,然后更新缓存。

    作者回复: 源码里我也有一个例子,思路是不要每次都evaluate脚本而是把脚本转变为一个方法parse后缓存起来这个Script,以后直接invokeMethod来使用

    2020-04-20
    3
    12
  • 汝林外史
    1. 对于老师说的autoComplete的场景是不是Trie树更适合一些? 2. 这个WeakReference可能导致内存溢出的典型就是ThreadLocal,虽然ThreadLocalMap的entry的key是weakReference,但是value是强引用,当用线程池的时候,就会内存溢出,还是要自己remove才行。 3. 对于问题1,应该是用软引用更好一些,用弱引用总是被gc回收就失去了缓存的意义。对于问题2不是很了解。

    作者回复: 1. 👍 是的,这种场景字典树更合适,不过我这边都是拿着实际的案例整理成文的,主要是希望告知大家我们认为的一份数据在程序中不一定是一份这个误区

    2020-04-20
    12
  • 👽
    应该用哪种引用,首先考虑的肯定是四大引用的区别。 1.强引用:最常见的一种,只要该引用存在,就不会被GC。 2.软引用:内存空间不足时,进行回收。 3.弱引用:当JVM进行GC时,则进行回收,无论内存是否充足。 4.虚引用:这个不提了,因为我也完全不懂。 软引用和弱引用,这两个,让我选,肯定是选软引用。因为弱引用,被回收的频率更高。缓存,如果经常被回收的话,就达不到最大利用率。 但是这里又要说点额外的,单说缓存设计,还要涉及其他的因素。包括缓存大小,缓存的过期时间等。让我来说的话,我可能会考虑使用现有的缓存实现,或者是redis。自己实现一套缓存,成本略高。

    作者回复: 不错

    2020-04-18
    2
    9
  • 一个汉子~
    之前还遇到一个,一个导出功能,拥有管理员权限的人几乎没有限制,造成了全表查,再加上框架禁止join,所以又把外键拉出来做了一次in查询,也是全表扫,大量的Bo对象和超长sql,直接把系统oom了

    作者回复: 很常见的问题,还有包括参数未传导致mybatis条件没有拼接上去,导致全表查询的oom

    2020-04-18
    3
    8
  • 不能忍的地精
    我遇到一个OOM,是这样的 1. 首先有一个线程池,线程数量是20个,但是线程队列容器的数量是Integer的最大值,所以拒绝策略几乎无效,大概没过3秒往线程池提交3个任务 2. 任务里面有一个Restemplate,没有设置超时时间,超时时间为-1,并且里面维护的连接池是5个,小于线程池数量 3. 出现5个连接都超时,任务卡住了,但是还是不断的往任务队列里面添加任务,最终导致OOM

    作者回复: 😥

    2020-04-24
    7
  • ddosyang
    老师想问一下,在WeakHashMap的那个例子里,可不可以直接用String name当作Key,而不是用User做Key。这样是不是也可以解决问题?

    作者回复: 是,不过这就改了设计了

    2020-04-20
    2
  • Carisy
    老师请教一个问题,在使用CompletableFuture的时候出现了很奇怪的场景,就是buffers飙升出现oom,应用程序使用内存并不多,处理逻辑也相对简单就是调用接口通过http上传下载文件

    作者回复: buffers是指什么buffer?这个和CompletableFuture没有关系,上传下载都会用到缓冲区,如果并发大的话可能是会OOM

    2020-06-05
    1
  • 旅途
    有点没懂 java.lang.IllegalArgumentException: Request header is too large 的意思不就是request header过大了么 为什么开发人员还设置的那么大?

    作者回复: 这个异常原因是请求头数据过大超过了限制 不是指tomcat请求头参数配置过大

    2020-04-28
    2
    1
  • pedro
    问题一,弱引用是在内存不足时被 gc 掉,而软引用是只要 gc 就回收掉,自然就不能用来做缓存,否则动不动就缓存失效,数据库怕是要被玩坏哦,因为适合做缓存的是弱引用。 问题二,没用过 groovy,希望看到别人的解答。😄

    作者回复: 不太对,可以再查一些资料或做一些实验看下软和弱的区别

    2020-04-18
    3
    1
收起评论
显示
设置
留言
15
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部