最近在进行项目版本迭代的过程中,我们自测通过、联调OK的情况下,我们提测,在测试的同事小唐针对我们的程序进行覆盖性压力测试的时候,突然间程序假死,或者响应迟钝。日志不再打印。过了一会,Java服务自动宕机。 服务需要重启,重启后继续同样测试问题能够复现。
没办法,这种问题只能我们开发人员来解决, 那么其它的小伙伴,开始去分析堆内存文件报告。
-XX:+HeapDumpOnOutOfMemoryError: 指定文件输出路径及文件名称
让虚拟机在出现内存溢出异常时dump出当前内存堆转储快照以便事后进行分析。带上这种参数之后运行Jvm,如果出现相应的内存溢出异常,会在目录下形成一个异常时候的内存dump文件(如java_pid7126.hprof文件1-2G),将这个文件使用Memory Analyse Tool工具打开就可以看到当前dump内存空间的分析内容。
不过我的思路却在于程序假死?会不会是内存不够?
JVM内存不够一般两大原因:
1、内存泄露——代码问题(找代码原因)
2、可能你给的内存真的不够 (代码存在优化的空间、框架依赖太多了)
想到这我没有直接去分析堆内存文件的对象占用内存的情况;
我先实时的去用指令监听这个服务的内存占有情况以及垃圾回收情况;使用jdk给我们提供的工具jstat,命令如下:
查看进程号:ps -ef 监听服务:jstat -gc 进程号 1000(每隔多少毫秒打印一次) jstat -gcutil 10777(进程号) 1000
伊甸园以及老年代的内存使用情况类似下图:
我发现每次ygc以后,老年代内存使用率就会提升,然后我对这个java 进程进行持续关注,执行全面压测脚本,发现在经过短暂的时间之后,老年代内存使用情况占比达到90%以上,然后疯狂FullGC,非常频繁。一秒钟2-5次。非常可怕。然后一段时间之后,程序假死,日志不再打印,持续几秒,进程自动终止。服务宕机。我直接给这个服务 分配 2G的最大内存,给新生代600m,1.4G全留给老年代。然后我怀疑是内存给小了,然后我直接调整参数 -Xmx=2G -Xmn500m,重启服务倒要看看这个老年代到底能用多少,然后持续全面压测,
1)、如果给多少用多少,那么肯定是代码有问题,再去分析内存dump文件定位代码问题不迟;
2)、如果最终老年代内存使用,定格在某个值发送不动了,那么证明确实是我给内存分配小了;然后根据它的内存最终占有情况,给他分配一个合理的值,即ok,解决问题!老年代 稳定在1.2G 不再继续增长,FGC频率不再飙升,程序压测无问题,上线。问题完美收官。然后告诉其它小伙伴,你们别瞎鸡儿折腾了!
简单说下堆栈:
栈——控制程序执行的流程;
堆——存储程序需要的数据;
JVM堆栈分离的设计思想,让垃圾回收变得可能。
JVM调优调什么?
JVM垃圾回收机制调优
垃圾回收机制这里不详讲
JVM内存调优:
比如说 你一个服务需要用2G的内存,然后你当前机器却默认给它分配了1G;
比如说 你一个服务需要512M的内存,然而机器给它默认分配1G的内存;
JVM内存结构
1、程序计数器
每一个线程都会有一个程序计数器,记录线程指令执行到的位置。
2、虚拟机栈——Java方法
每一个线程都会开辟内存空间,栈,存的是栈帧(一个方法一个栈帧);
描述java方法执行的内存模型;
3、本地方法栈——非Java方法
描述Native方法执行的内存模型
4、堆
存储对象,new 创建的对象
5、方法区(1.8弃用) 1.8永久代也没有了
类信息,方法信息…
java7之前,方法区位于永久代(PermGen),永久代和堆相互隔离,永久代的大小在启动JVM时可以设置一个固定值,不可变;
java7中,存储在永久代的部分数据就已经转移到Java Heap或者Native memory。但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了native memory;字符串常量池(interned strings)转移到了Java heap;类的静态变量(class statics)转移到了Java heap。
java8中,取消永久代,方法区存放于元空间(Metaspace),元空间仍然与堆不相连,但与堆共享物理内存,逻辑上可认为在堆中
Native memory:本地内存,也称为C-Heap,是供JVM自身进程使用的。当Java Heap空间不足时会触发GC,但Native memory空间不够却不会触发GC。
如何进行JVM内存调优?
java -jar -Xmx1G -Xms1G -Xmn300m ****.jar
-Xmx 最大内存(堆) max
-Xms 初始化(最小)内存,可以指定跟Xmx相同,避免重新分配内存耗费时间 small
-Xmn Java8(伊甸园+幸存区)——新生代 new 新生代是指刚new不久的对象,一般占jvm内存3/8,老年代占5/8
那么Xmx减去Xmn就是老年代大小
堆内存不足 会出现什么问题? OutofMemerryError
堆内存空间不足,但是最终抛出错误的地方,还是在栈里面
经典案例——
1、分页查询 没有加分页参数 一次性查询1000万条数据;(确实空间不足)
2、频繁操作string 没有 用 stringBuffer 或者 sringBuilder;(内存泄漏)
3、读写的stream 流、连接...开辟了资源没有释放,只管开辟,不管回收;
4.微服务启动,springcloud 很多组件,IOC很多bean实例;
-Xss 线程栈大小,这个值分配得越大,那么当前程序可以同时创建的线程数就会越小
-Xss不足会出现什么问题?—— 栈内存不足,StackOverFlowError
经典案例——
1、无限递归;
StackOverflow是栈溢出
方法栈溢出,方法里面的引用、深度、太多了