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。

分析

通常状态下,我见到 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
今日推荐