概述
jvm中除了程序计数器,其他的区域都有可能会发生内存溢出
内存溢出是什么?
当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出
内存溢出和内存泄漏有什么区别?
内存泄漏是由于使用不当,把一部分内存“丢掉了”,导致这部分内存不可用。
当在堆中创建了对象,后来没有使用这个对象了,又没有把整个对象的相关引用设为null。此时垃圾收集器会认为这个对象是需要的,就不会清理这部分内存。这就会导致这部分内存不可用。
所以内存泄漏会导致可用的内存减少,进而会导致内存溢出。
用到的jvm参数
下面为了说明溢出的情景,会执行一些实例代码,同时需要给jvm指定参数
- -Xms 堆最小容量(heap min size)
- -Xmx 堆最大容量(heap max size)
- -Xss 栈容量(stack size)
- -XX:PermSize=size 永生代最小容量
- -XX:MaxPermSize=size 永生代最大容量
堆溢出
堆是存放对象的地方,那么只要在堆中疯狂的创建对象,那么堆就会发生内存溢出。
下面做一个堆溢出的实验
执行这段代码的时候,要给jvm指定参数
//jvm参数:-Xms20m -Xmx20m public class HeapOOMTest { public static void main(String[] args){ LinkedList<HeapOOMTest> l=new LinkedList<HeapOOMTest>();//作为GC Root while(true){ l.add(new HeapOOMTest());//疯狂创建对象 } } }
-Xms20m -Xmx20m作用是将jvm的最小堆容量和最大堆容量都设定为20m,这样就不会动态扩展jvm堆了
这段代码疯狂的创建对象,虽然对象没有声明变量名引用,但是将对象添加到队列l中,这样队列l就持有了一份对象的引用
通过可达性算法(jvm判断对象是否可被收集的算法)分析,队列l作为GC Root,每一个对象都是l的一个可达的节点,所以疯狂创建的对象不会被收集,这就是内存泄漏,这样总有一天堆就溢出了。
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.LinkedList.linkLast(Unknown Source) at java.util.LinkedList.add(Unknown Source) at test.HeapOOMTest.main(HeapOOMTest.java:23)
程序发生内存溢出,并提示发生在Java heap space
分析解决方法
思路
用visualVM工具分析堆快照
如果发生内存泄漏:
step1:找出泄漏的对象
step2:找到泄漏对象的GC Root
step3:根据泄漏对象和GC Root找到导致内存泄漏的代码
step4:想法设法解除泄漏对象与GCRoot的连接
如果不存在泄漏:
1. 看下是否能增大jvm堆的最大容量
2. 优化程序,减小对象的生命周期
前期准备
当发生堆溢出的时候,可以让程序在崩溃时产生一份堆内存快照
产生堆内存快照的方法:
给jvm加上参数XX:+HeapDumpOnOutofMemoryError,这样就会在程序崩溃的时候,产生一份堆内存快照
分析堆内存快照我建议用jdk自带的可视化监视工具visualVM,位置在jdk安装目录下的bin,如果是在Linux环境的话,可以把快照传到window。因为分析工具会占用很大的内存,不建议在服务端进行分析。
实战
下面对刚才程序产生的堆内存快照进行分析。
打开visualVM,装入刚刚生成的快照,打开类标签页
队列和疯狂创建的对象几乎占满了整个栈,想要让垃圾收集器回收这些对象,要让他们与GC Root断开连接
双击HeapOOMTest类,跳转到实例标签页,可以查看这个类的所有实例
在实例上右键——显示最近的垃圾回收根节点,可以看到这个对象与根节点的连接
只要断开HeapOOMTest对象与LinkedList的连接,这些疯狂创建的对象就会被收集了
栈溢出
调用方法的时候,会在栈中入栈一个栈帧,如果当前栈的容量不足,就会发生栈溢出StackOverFlowError
那么只要疯狂的调用方法,并且有意的不让栈帧出栈就可以导致栈溢出了。
下面来一次栈溢出
//jvm参数:-Xss128k public class StackSOFTest { public void stackLeak(){ stackLeak();//递归,疯狂的入栈,有意不让出栈 } public static void main(String[] args){ StackSOFTest s=new StackSOFTest(); s.stackLeak(); } }
jvm设置参数-Xss128k,目的是缩小栈的空间,这样栈溢出“来的快一点”
程序中用了递归,让栈帧疯狂的入栈,又不让栈帧出栈,这样就会栈溢出了。
运行结果:
Exception in thread "main" java.lang.StackOverflowError at test.StackSOFTest.stackLeak(StackSOFTest.java:17) at test.StackSOFTest.stackLeak(StackSOFTest.java:17)
运行时常量池溢出
这里储存的是一些常量、字面量。如果运行时常量池内存不足,就会发生内存溢出。从jdk1.7开始,运行时常量池移动到了堆中,所以如果堆的内存不足,也会导致运行时常量池内存溢出。
下面来一次运行时常量池溢出,环境是jdk8
只要创建足够多的常量,就会发生溢出
/** * jvm参数: * jdk6以前:-XX:PermSize=10M -XX:MaxPermSize=10M * jdk7开始:-Xms10m -Xmx10m * */ public class RuntimePoolOOM { public static void main(String[] args){ int i=1; LinkedList<String> l=new LinkedList<String>();//保持常量的引用,防止被fullgc收集 while(true){ l.add(String.valueOf(i++).intern());//将常量添加到常量池 } } }
因为jdk6以前,运行时常量池是在方法区(永生代)中的,所以要限制永生代的容量,让内存溢出来的更快。
从jdk7开始,运行时常量池是在堆中的,那么固定堆的容量就好了
这里用了链表去保存常量的引用,是因为防止被fullgc清理,因为fullgc会清理掉方法区和老年代
intern()方法是将常量添加到常量池中去,这样运行时常量池一直都在增长,然后内存溢出
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.lang.Integer.toString(Unknown Source) at java.lang.String.valueOf(Unknown Source) at test.RuntimePoolOOM.main(RuntimePoolOOM.java:30)
提示在heap区域发生内存溢出,果然运行时常量池被移到了堆中
方法区溢出
方法区是存放类的信息,而且很难被gc,只要加载了大量类,就有可能引起方法区溢出
这里将不做演示了,想试试的可以用cglib创建大量的代理类
分析
工作中也有可能会遇上方法区溢出:
当多个项目都有相同jar包的时候,又都存放在WEB-INF\lib\下,这样每个项目都会加载一遍jar包。会导致方法区中有大量相同类(被不同的类加载器所加载),又不会被gc掉。
解决方案:
- 在应用服务器中建立一个共享lib库,把项目中常用重复的jar包存放在这里,项目从这里加载jar包,这样就会大大减少类加载的数量,方法区也“瘦身”了
-
如果实在不能瘦身类的话,那可以扩大方法区的容量,给jvm指定参数-XX:MaxPermSize=xxxM
相关推荐
### JVM内存溢出详解 #### 一、基本概念与理解 **JVM内存溢出**是一种常见的运行时错误,指的是程序在执行过程中因为...通过以上详细的分析和策略,可以有效地预防和解决JVM内存溢出问题,提高程序的稳定性和性能。
简单的判断JVM内存溢出的方法
本文主要讨论了 JVM 中的内存溢出问题,包括内存溢出的定义、内存泄漏和内存溢出的区别与联系、JVM 垃圾回收机制、内存溢出的原因分析、常见的四种内存溢出情况以及解决方案。 一、内存溢出的定义 内存溢出是指...
理解JVM内存结构和内存分配机制对于避免内存溢出(OutOfMemoryError)、提升程序性能、减少垃圾回收开销至关重要。开发者应关注内存配置、对象生命周期管理以及适当的垃圾回收策略,以优化应用程序的性能和稳定性。
【JVM内存溢出】指的是Java虚拟机在运行过程中,由于内存分配不当或者内存使用过度导致内存无法正常管理,从而抛出错误的情况。这通常会导致应用程序崩溃或性能急剧下降。在Tomcat这样的Java应用服务器中,内存溢出...
给定的部分内容中提到了一些JVM的启动参数,这些参数直接关联到内存溢出配置,下面我们来逐一解析: - **-Xms1024m**:设置JVM初始分配的堆内存大小为1024MB。这意味着JVM启动时,会立即申请1024MB的物理内存。 - *...
HeapAnalyzer是一款强大的工具,专为分析Java应用程序的内存状况,特别是针对内存溢出问题进行诊断。本文将详细介绍HeapAnalyzer的使用、功能以及如何通过它来排查和解决Java OOM问题。 一、HeapAnalyzer简介 Heap...
MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的JVM内存分析工具,尤其适用于诊断Java应用程序的内存泄漏问题。在Java开发过程中,内存溢出(Out Of Memory)问题常常会导致程序异常终止,而MAT就是解决这类...
### Eclipse Memory Analyzer (MAT) 分析 Tomcat 内存溢出详解 #### 一、引言 在软件开发过程中,特别是在 Java 应用程序中,内存管理是非常关键的一环。当应用程序遭遇 `OutOfMemoryError` 异常时,通常意味着...
综上所述,解决此类问题需要综合运用JVM内存模型知识、代码分析技巧以及使用专业工具进行诊断。通过以上步骤,开发者可以定位内存溢出的根本原因,从而优化代码和调整JVM配置,防止类似问题再次发生。
通过对JVM堆内存溢出的分析,我们可以了解到当一个线程出现OOM异常时,它所占据的内存资源会全部被释放掉,从而不会影响其他线程的运行。 首先,让我们来了解一下JVM内存模型。JVM的内存模型主要分为五个部分:方法...
而压缩包中的“内存溢出分析工具”可能包含了其他辅助分析内存问题的资源或文档,如使用教程、示例文件等。 总之,HeapAnalyzer456.jar是Java开发中不可或缺的内存诊断工具,通过它可以深入剖析内存溢出的原因,...
了解并监控JVM的内存使用情况,对于预防和解决内存溢出错误至关重要。通过动态地调整`-Xms`和`-Xmx`参数,可以在运行时根据实际需求优化内存分配,从而提高程序的性能和稳定性。此外,定期检查和分析`heapSize`、`...
当JVM遇到问题,如内存溢出或长时间的垃圾回收,它可能会生成堆栈转储(dump)文件,这些文件包含了JVM在特定时刻的详细状态,包括对象、类加载器、线程、堆和栈信息。"Jvm堆栈dump文件分析"是指通过特定工具对这些...
在生产环境中,持续监控Tomcat的内存使用情况,设置合理的JVM内存参数,并定期进行内存分析,以便尽早发现和解决问题。 通过以上步骤,我们可以利用MAT有效地诊断和解决Tomcat的内存溢出问题。理解MAT的使用方法...
通过本实验,旨在深入理解JVM内存管理机制以及各种内存区域的特点,并通过具体的编程实践来触发并分析这些异常,进而提升对Java应用程序性能调优和故障排查的能力。 #### 实验目标 1. **理解内存区域与内存区域...
当遇到内存溢出问题时,ha456能够分析堆dump文件,识别出导致内存消耗过大的对象和数据结构。通过分析对象的生命周期和引用关系,开发者可以追踪到导致内存无法释放的原因,进而优化代码,避免内存浪费。 最后,jca...
综上所述,解决JVM运行时内存溢出问题需要从多个角度进行:合理调整JVM内存参数、优化索引构建算法、监控系统状态并进行必要的硬件升级,甚至考虑采用分布式计算。只有这样,我们才能确保搜索引擎在创建索引时高效、...
【ha456.jar(IBMHeapAnalyzer)JVM内存分析工具】是一款由IBM开发的专业工具,主要用于诊断Java虚拟机(JVM)的内存问题。它能够解析和分析JVM生成的内存转储文件(通常称为heap dump或hprof文件),帮助开发者识别...