100个java程序员都会遇到的项目问题,到底如何解决内存溢出?

运用过 Java 计算机语言的,一个算两个,不管大神还是渣,应当都遇到过内存溢出的异常 OutOfMemoryError,问题看上去相似,可是引起的缘故却各有不同,今日给大伙儿详细介绍1个 OutOfMemory 实例,是我的好朋友九皋子提供的:一次怪异的 Java OutOfMemory 问题。

小编整理了一些java进阶学习资料和面试题,需要资料的请加JAVA高阶学习Q群:664389243 这是小编创建的java高阶学习交流群,加群一起交流学习深造。群里也有小编整理的2019年最新最全的java高阶学习资料!

也许大伙儿以前都遇到过 HashMap 由于其非线程安全的多线程高并发实际操作,造成 cpu 飙高的现象,不过这一问题在 Java 8 里早已处理掉了,其根本原因很多人写过,你去检索「HashMap Infinite Loop」都可以见到许多人在写这一事,因此我就不再熬述了。文中和大家聊的是另一个两个状况 —— 内存溢出。

现象

老同事丢了1个运行内存分析的链接进来,我见到一个线程占用的运存十分高,问题比较突出,因此我展开看了下线程栈:

可以很清晰的见到,程序在启用一个 Map 对象的 toString 方法,直至抛出去 java.lang.OutOfMemoryError 为止,因此这个栈顶能见到 OutOfMemoryError 的逻辑思维由于虚拟机参数配备了 -XX:+HeapDumpOnOutOfMemoryError。

有关 JVM 参数,给大伙儿强烈推荐九皋子做的1个小程序 JVMPocket,大家可以在微信中搜索 JVMPocket 使用。

JVMPocket 是1个详细介绍 JVM 参数的微信小程序。平日常常许多人问有关的问题,什么样主要参数都可以处理哪些难题,可是烦扰参数很长没办法记忆。拥有 JVMPocket 以后,直接找出相匹配的参数就可以见到主要参数的实际涵义、使用方法、默认值以及大家的使用意见等,期待该微信小程序也可以帮到大伙儿,如果你有自身的 JVM 参数经历,都可以到相匹配的参数下边留言板留言让更多人了解它背后的故事。

为了便于大家科学合理查询和检验自身系统的 jvm 参数目录,PerfMa 公司还特地给大家提供了1个完全免费的web版本的 jvm 参数解析好产品 XXFox.您将可以查看,检查,升级,优化,一键生成自己的系统的 jvm 参数列表,大家可以体验一下。

不过这一参数只会生效一次,不会每一次 OOM 的那时候都做运存 dump,大家都可以想象一下,假如由于代码的问题产生持续的 OOM,那持续做 dump 也没必要性,因此 JVM 里操纵这一参数总是在初次产生 OOM 的时候做一次运存 dump。

扫描二维码关注公众号,回复: 5072254 查看本文章

分析

通常状态下,我见到 OutOfMemoryError 栈后,会让同事去看我以前写的有关 OOM 的稿子了,换句话说,这个线程栈里看到了 OOM,可是内存泄漏未必是和这个线程有关,或者仅仅临门一脚罢了。不过后边细看了下这个线程占的内存,确实挺高,有 2G 多。就这一典型案例来讲,内存溢出就和当今这个线程有关,有时候不能太相信自己的经验,实际问题还是得深入分析才好。

为何这一线程会占有那么大的运存呢?见到一整块栈后边都在做字符串的拼凑扩充动作,毫无疑问应该是 toString 方式释放的。难道说确实有个 2G 的字符串?问询同事,有人说绝对并不是存有那么大的字符串,你可知道,程序员们通常全是这样的回应,不应该啊,不可能啊……因此我始终怀疑他们说的,显然是存有这么大的字符串的,就是他们想不起来而已。

跟男同事电话沟通交流以后,我给他打印了1个 Map,这一Map 就是ConcurrentHashMap,也就是说线程安全的,这一 Map 里的 Value 是一个 HashSet,这一 HashSet 是非线程安全的,而且存有好几个线程修改这一 Set 的状态,那会否由于高并发造成的呢?HashSet 里我觉得只是1个 HasMap 的结构,我认为十分可能,因此要男同事自个去仿真模拟下这一情景,看可否再现出来。

我再次看他们的内存 dump,果真察觉了一些问题,在打印那些 HashSet 过程中,next 字段是循环连起来的,因此主要明确了死循环的存在。没过一段时间,男同事也再现出来,大致逻辑思维如下:

特别注意,这一是在 JDK6 或是 7 下运作才会再现,JDK8 下找不到这一现象。Demo 里就是说2个线程同时对 HashSet 开展改动,将会产生的不良影响是,里边的 HashMap 由于要扩充而且做 rehash,进而经常出现死循环的状况,当有线程要打印这一 HashSet 的时候,会启用其 toString 方式,再看一下父类 AbstractCollection的 toString 的逻辑。

逐个遍历,之后将值塞到 StringBuilder 里,假如恰巧事先由于多线程的并发使用造成了死循环链的发生,你就将会会造成这个 StringBuilder 特别大,而且还会持续扩充。如同上方的堆栈见到的相同,直接产生的不良影响也是出现内存溢出。

运存足够时候的 OutOfMemory

针对男同事线上遇到的那个问题,OOM 提示是 Requested array size exceeds VM limit,这一提示信息我还是首次遇到。倘若你的内存特别大,有足够的多余空间,可是,如果你要建立1个数组的时候,当你的数组的总长度超出了Integer.MAX_VALUE-2,那你将会见到1个这个提示信息的 OutOfMemory,这就是也是你可以建立数组的最高长度。许多人或者都没有注意到的这一点,就算个小彩蛋吧。

小编整理了一些java进阶学习资料和面试题,需要资料的请加JAVA高阶学习Q群:664389243 这是小编创建的java高阶学习交流群,加群一起交流学习深造。群里也有小编整理的2019年最新最全的java高阶学习资料!

猜你喜欢

转载自blog.csdn.net/weixin_42784331/article/details/86547666