最近入职了一家公司,项目比较庞大,用的还是云桌面,2C10G的最大配置(卑微外包就只能这样),首先发现10G内存最多只能跑到60%,发现是一个叫做sysMain的服务在限制内存的占用,从任务管理器找到他把他做掉(停止并且停止自启)
然后发现我的电脑内存上去了,可以跑到95%了,但是没diao用,依旧OOM。
找到IDEA-setting-compiler,
把默认700的那个java heap space,直接开到2048,启动!报错。
Error occurred during initialization of VM Could not reserce enought space for 1740800KB object heap,别问为嘛是1700M,因为我1700M都不行,最后只能开到1500,项目可以启动,但是启动后不到一分钟就oom堆内存爆了。
代码又不是我写的,为什么要这样子对待我!我怒了一下,也没什么diao用,找到IDEA的安装路径下的bin目录,找到了idea64.exe.vmoptions文件,设置了-Xms256m -Xmx2048m,重启IDEA,部分人走到这里估计就可以正常启动了。这里有个坑哈,32位的系统和非64位的jdk版本改idea64.exe.vmoptions文件好像是没有用的,但是只要IDEA和JDK有一个是64位的,改idea64.exe.vmoptions就行。
然后我还是启动不了项目…我简直要崩溃了,求助了下水友群,我佬缘起bro说做掉IDEA,直接java -jar,好好好,我觉得可行,毕竟IDEA的确是吃内存的大户。我起手就是mvn package打jar包,然后接java -jar加VM options真的能启动啊。对不起,java -jar xxx.jar -Xms256m -Xmx2048m 也米有用,我打算重新找工作了(bushi)
下午,峰回路转,自己动手解决问题算什么事,更换jdk!8换11,jdk11的垃圾回收器更好,JDK11是优化过后的,他们的工程师吊打100个我,总能启动的吧?
啊!jdk11只要2分钟就启动了!jdk8要9分钟!啊啊啊啊啊!jdk11不会oom,jdk11,爱你爱你!
后续——————————————————————————————————————————开发阶段还是要用jdk8,怕不知不觉的就把JDK11的新包新类写进去了,自测还是走jdk11。至于jdk11为啥这么生猛,我得查一下。
在Java开发中,JDK8与JDK11在Java虚拟机(JVM)的内存配置上存在一些显著差异,这可能会影响应用程序的性能和稳定性。以下是对这两者在内存管理方面的比较,以及为什么某个Java应用在JDK8中出现OOM(Out Of Memory)崩溃而在JDK11中正常运行的分析。内存管理的主要差异
元空间(Metaspace)
JDK8:使用**永久代(PermGen)**来存储类元数据,默认大小为20.75MB,且需要手动设置最大值(-XX:MaxPermSize),否则可能导致OOM。
永久代的内存是固定的,如果类加载过多,容易耗尽这一部分内存。
JDK11:引入了元空间(Metaspace),不再使用固定大小的永久代,而是动态分配内存。默认情况下,元空间的最大值为无限(除非手动设置-XX:MaxMetaspaceSize)。
由于元空间可以根据需要扩展,因此对于类加载和动态生成代码的支持更好,减少了OOM的风险1
2
。
垃圾回收器(Garbage Collector)
JDK8:默认使用Parallel GC,其设计目标是优化吞吐量,但在某些情况下可能导致较长的停顿时间。
JDK11:默认使用G1 GC,其设计目标是减少停顿时间,提高应用程序的响应性。G1 GC通过将堆划分为多个区域并优先回收垃圾最多的区域来实现这一点3
4
。
容器支持
在JDK10之前,JVM无法识别容器内存和CPU限制,这可能导致在资源有限的环境下出现OOM。而从JDK10开始,JVM能够根据容器设置自动调整内存限制,这一改进在JDK11中得到了延续和优化2
。
OOM崩溃原因分析
某个Java应用在JDK8中启动时出现OOM崩溃,而在升级到JDK11后却能正常运行,可能有以下原因:元空间管理改进:JDK11中的元空间动态分配特性使得类加载和动态代码生成更加灵活,不再受到固定大小限制。应用程序如果频繁加载类或生成动态代码,在JDK8中可能会迅速耗尽PermGen,而在JDK11中则不容易发生这种情况1
2
。
垃圾回收机制优化:JDK11使用G1 GC,它能够更有效地管理内存并减少停顿时间。这意味着即使在高负载情况下,也能更好地回收无用对象,从而降低OOM发生的概率3
4
。
容器环境适应性:如果该应用运行于容器环境中,JDK11对容器资源限制的支持意味着它可以更好地利用可用资源,从而避免因资源不足而导致的OOM2
。
综上所述,JDK11在内存管理和垃圾回收方面的改进,使得它能够更有效地处理内存分配和回收,从而降低了OOM崩溃的风险。这些特性使得开发者在迁移到新版本时,可以获得更好的性能和稳定性。
噢这个AI回答的有点问题,永久代是JDK7以及7之前的,JDK8是元空间了哈。
缘起bro给的答复指向了元空间,元空间存放的内容主要有静态常量,
还有JDK7及之前的常量池,以及类的元数据信息等,
估计OOM的主要原因还是元空间太小,只是为什么报错是java heap space呢?这个后面再看。
为了验证,我切回了JDK8,不加任何VM options启动项目,9分钟后启动成功,随后就报错Exception in Thread “xxxService” Exception in Thread “netty-client-1” Exception in Thread "rnetty-redis-2.3.1"之类的线程异常,但是没有详细的异常信息,期间想试试jconsole、jstack等分析工具查看,结果直接就要么卡住要么连不上。好,说明就是JDK带来的成功,等于是一口气排除了其余的环境因素。
本次JDK8新增VM options参数:
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
原神,启动!好好好,依旧是半死不活9分钟启动的死样子,约一分钟后,熟悉的OOM:java heap space。我泪流满面:回来了,熟悉的OOM,一切都回来了
but这一次,我想了一下,继续加参数,加上之前无效的VM 参数
-Xms256m -Xmx2048m
最后运行时的VM options为:
-Xms256m -Xmx2048m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
然后,9分钟的漫长启动,10分钟的稳定运行,OOM,没有出现,Exception in Thread xxx,没有出现,撒花!
对了,说明下这个项目,jconsole查看发现启动了约24450个类,线程稳定在177-180,堆内存刚启动是400,然后稳定在600-800M,一开始还不觉得这个危险,后来发现我的堆内存也就800M出头…
害,虚拟机最大内存与你电脑剩余的内存有一点关系的,我没启动前端时可以分Xmx2048,启动前端后只能Xmx1400了,真正的堆内存只有770M左右,资源太紧张了
本次学到了啥,额好像没学到啥,学到了不放弃?遇到问题先把工资混到手再说,别想着跑路,多告诉别人,你的环境出问题了,并且别人帮不上你?你换了电脑也不太行?总之算是比较完美的解决了
噢以后也可以尝试升级一下JDK,当然前提是你的代码得兼容JDK哈,而且不要直接上生产环境,毕竟你升级了JDK,不代表着项目里引入的依赖也能支持JDK的升级,项目已经有了,再临时升JDK,是一个剑走偏锋的危险路子,不然为啥你强任你强,我用1.8呢
----------------------------------------------------------2024-10-12------------------------------------------------------------好吧妈的是我发布文章的当日,又又又OOM了!
java.lang.OutOfMemoryError:unable to create new native thread
我恨!
查了下,这个报错的大致意思是,没有多余的内存去创建线程了,所以是哪个小贼这么占用我紧张的虚拟机内存资源?
看了下日志,发现是for循环里调用mq发送群组,然后真正的执行是放到一个线程池里异步去做的,我觉得是我虚拟机内存紧张了,毕竟创建线程后还需要分配内存,所以找到了这个线程池,查看了详细的参数,核心线程数是4个,最大线程数20,我觉得我的机子他不行,所以核心线程数砍到了C+1,也就是核心数+1,改成3,最大线程,我这个算IO密集型任务,而且本地内存真的很吃紧啊,改成8,就先不看线程池的优化了,毕竟我的主干任务是我的代码自测。不说了,原神,启动!让我再走一边测试案例,看看没好结果。
----------------------------------------------------------2024-10-12------------------------------------------------------------
好吧妈的是我发布文章的当日,又又又OOM了!
本次报错内容还是这个:java.lang.OutOfMemoryError:unable to create new native thread
我恨!距离上一次我恨只有30分钟,果然没好结果。上次的OOM是调用MQ,此次OOM是saveBatch方法,里头传参是一个List.stream.map(getXXX).toList就报错了,所以根本原因不是这个List,还是虚拟机内存太紧张了,所以怎么处理呢?让我想想。想到了,我只是自测,上JDK11!hh开玩笑的,jconsole发现GC时间很长,copy上的GC时间长达16秒,好,无脑上G1,又不是我写的代码,我只是想自测罢了为什么要这么难啊,最后最后的VM options:
-Xms256m -Xmx2048m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+UseG1GC
但愿本次就是终章,今日不想更新这篇文章了!
哈哈我回来了,项目启动不了了,还是OOM/苦笑.jpg,对不起我鉴定选择拥抱JDK11,哥就是要用JDK11测试我的代码/实在是解决不了了,我能用的内存就这么多,项目又大又复杂,我也只是启动下项目自己测试一下我的功能罢了,累觉不爱了
说下正儿八经的总结吧,项目启动OOM,首先看看是不是虚拟机分配内存太小了,先看看是哪个内存爆了,堆内存还是栈溢出还是元空间,先考虑增大虚拟机内存,如果要考虑代码优化,就要看看哪个爆了,回想面试题,什么区域放的什么玩意,内存爆了的报错信息,看看是不是for循环创了多个新对象,或者是不是什么连接资源没有关闭,一直在创建,一直没关闭,看看是不是递归没有出口,死锁了,或者方法调用链太深了,总之正常一点的项目,扩大下内存就行,不太行的项目或者电脑,才需要你去搞JVM参数,甚至改垃圾处理器,甚至升级重构整个项目,那是另外的事了,单元本次复盘可以帮到有需要的人,撒花,再见
噢,对,VM参数在这里配置:
然后:选择Modify options
再选择 add VM options
2024-10-22:系统启动报错,之前用JDK11启动项目很ok,但是因为项目是一个画布,涉及到多个项目,我写的功能只是一个画布逻辑处理的组件,需要前置项目发送mq请求给我,所以,鄙人其实是10个G的内存启动一个前端项目+两个大后端项目,今天也是报错:
Increase physical memory or swap sapce
check if swap backing store is full
Decrease java heap size
Decrease number of java threads
Drease hava thread stack sizes
谷歌翻译了一下:
增加物理内存或交换空间
检查交换后备存储是否已满
减少 Java 堆大小
减少 Java 线程数
调整 Java 线程堆栈大小
害,只好又调整JVM参数,这次调整为-Xms256m -Xmx1024m -Xss512k
-Xss是每个线程栈的大小,貌似JDK默认是1m吧,但是这个项目实在是要太多线程了,把内存又打爆了,算是又学到了一个新的JVM参数吧。害,我真的是芙辣,怎么这么难呢!定位一个bug而已,一直卡在环境问题上,跑路!
后记:新人进入项目组,搭建好了环境,半小时都启动不了项目,最好的方式还是向同事请求帮助,同事帮忙查看原因,排除了环境问题还是启动不了,最好还是向上反馈,毕竟现在都是唯结果论,你启动不来不去请教,不向外抛出问题,那就是包住问题,无论你启动的多辛苦,多委屈,别人都不会同情什么,部分项目具有独特的配置文件,你不去求助永远都启动不起来。
当然,如果同事不搭理你,你回头继续搞配置、环境,可以向领导申请,让他给你分配资源,这种情况最好自己先排查完环境问题,启动不起来最好重启电脑试试。如果领导、同事都不帮忙,那赶紧跑路,留着干嘛。