JVM堆与堆调优以及出现OOM如何排查

调优的位置——堆

Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般会把什么东西放到堆中?类,方法,常量,变量~,保存我们所有引用类型的真实对象;
堆内存中还要细分为三个区域:
1.新生区(0区/1区):类诞生和成长的地方,甚至死亡。其中的伊甸园区,是所有的new对象发生的地方。
2.养老区:从新生区经过GC之后存活下来的对象进入养老区
3.永久区:这个区域常驻内存的。用来存放DK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息,这个区域不存在垃圾回收!关闭VM虚拟就会释放这个区域的内存

在堆中主要存放加载的Class类级对象如class本身,method,field等等
在这里插入图片描述
幸存区0和1在堆内存中是一个动态变化的情况他是伊甸园区和养老区之间的一个过渡,顾名思义无法幸存的对象会被“kill”,GC垃圾回收主要在新生区中的伊甸园区和养老区进行。回收的方式分为轻GC与重GC,其中轻GC主要存在于新生区,只有当进入老年区的垃圾对象超过限额才会触发重GC(full GC),该操作主要存在于养老区。
在这里插入图片描述
在开发中可能遇到OOM的问题,表示内存不够,这个内存也就是JVM的堆内存,比如下面这个例子:
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
在这里插入图片描述
99%的对象都是临时对象,所以经过GC之后很少有进入到老年区中,因此OOM的情况也很少发生

永久区的演变

这个区域常驻内存的。用来存放IDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息。

jdk1.6之前︰永久代,常量池是在方法区;
jdk1.7︰永久代,但是慢慢的退化了,去永久代,常量池在堆中
jdk1.8之后:无永久代,常量池在元空间

在这里插入图片描述

OOM的出现

永久区内存崩溃的情况:一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类。不断的被加载。直到内存满,就会出现OOM;
在这里插入图片描述
默认情况下:分配的总内存是电脑内存的四分之一,初始化的内存是电脑内存的六十四分之一
但是这些内存参数是可以人为调整的
在这里插入图片描述
运行程序后,发现JVM参数发生了变化
在这里插入图片描述

可以看出,元空间逻辑上存在,但物理上并不存在

出现OOM如何去排查

可以使用内存快照工具分析对象,主要有:MAT(Eclipse),Jprofile(IDEA)两种工具。
向VM传入以下参数即可
在这里插入图片描述
当程序运行出现OOM,进入类所在的文件目录下去寻找Jprofile生成的hprof文件即可。
在这里插入图片描述

双击打开即可初步发现占用大量内存的对象,俗称“大对象”
在这里插入图片描述

查看线程(Thread Dump)即可发现程序中出现问题的所在行。
在这里插入图片描述
针对具体情况就可以进行JVM相关调优参数进行调优:
在这里插入图片描述
分享一些其他的JVM参数:
-Xmx4g:堆内存最大值为4GB。
-Xms4g:初始化堆内存大小为4GB。
-Xmn1200m:设置年轻代大小为1200MB。增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss512k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1MB,以前每个线程堆栈大小为256K。应根据应用线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=8:设置年轻代中Eden区与Survivor区的大小比值。设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:PermSize=100m:初始化永久代大小为100MB
-XX:MaxPermSize=256m:设置持久代大小为256MB
-XX:MaxTenuringThreshold=15:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过
可调优参数:
-Xms:初始化堆内存大小,默认为物理内存的1/64(小于1GB)
-Xmx:**堆内存最大值。**默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。
-Xmn:新生代大小,包括Eden区与2个Survivor区
-XX:SurvivorRatio=1:Eden区与一个Survivor区比值为1:1
-XX:MaxDirectMemorySize=1G:**直接内存。**报java.lang.OutOfMemoryError: Direct buffer memory异常可以上调这个值。
-XX:+DisableExplicitGC:禁止运行期显式地调用System.gc()来触发fulll GC
注意: Java RMI的定时GC触发机制可通过配置-Dsun.rmi.dgc.server.gcInterval=86400来控制触发的时间。
-XX:CMSInitiatingOccupancyFraction=60:老年代内存回收阈值,默认值为68
-XX:ConcGCThreads=4:CMS垃圾回收器并行线程线,推荐值为CPU核心数
-XX:ParallelGCThreads=8:新生代并行收集器的线程数
-XX:MaxTenuringThreshold=10:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
-XX:CMSFullGCsBeforeCompaction=4:指定进行多少次fullGC之后,进行tenured区 内存空间压缩

-XX:CMSMaxAbortablePrecleanTime=500:当abortable-preclean预清理阶段执行达到这个时间时就会结束

猜你喜欢

转载自blog.csdn.net/weixin_57535055/article/details/129232440