概述

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掉。

解决方案:

  1. 在应用服务器中建立一个共享lib库,把项目中常用重复的jar包存放在这里,项目从这里加载jar包,这样就会大大减少类加载的数量,方法区也“瘦身”了

  2. 如果实在不能瘦身类的话,那可以扩大方法区的容量,给jvm指定参数-XX:MaxPermSize=xxxM

    查看原文:http://blog.zswlib.com/2016/11/07/jvm内存溢出分析/

jvm内存溢出分析的更多相关文章

  1. JVM内存溢出分析java.lang.OutOfMemoryError: Java heap space

    JVM内存溢出查询java.lang.OutOfMemoryError: Java heap space查出具体原因分为几个预备步骤 1.在运行java程序是必须设置jvm -XX:+HeapDump ...

  2. BAT面试必问题系列:深入详解JVM 内存区域及内存溢出分析

    前言 在JVM的管控下,Java程序员不再需要管理内存的分配与释放,这和在C和C++的世界是完全不一样的.所以,在JVM的帮助下,Java程序员很少会关注内存泄露和内存溢出的问题.但是,一旦JVM发生 ...

  3. Tomcat中JVM内存溢出及合理配置及maxThreads如何配置(转)

    来源:http://www.tot.name/html/20150530/20150530102930.htm Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚 ...

  4. Tomcat中JVM内存溢出及合理配置

    Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识 ...

  5. JVM内存溢出及合理配置

    Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识 ...

  6. 定位JVM内存溢出问题思路总结

    JVM的内存溢出问题,是个常见而有时候有非常难以定位的问题.定位内存溢出问题常见方法有很多,但是其实很多情况下可供你选择的有效手段非常有限.很多方法在一些实际场景下没有实用价值.这里总结下我的一些定位 ...

  7. jvm 内存溢出问题排查方法

    如果你做TCP通讯或者map集合操作,并发处理等功能时,很容易出现 Java 内存溢出的问题.本篇文章,带领大家深入jvm,分析并找出jvm内存溢出的代码. jvm中除了程序计数器,其他的区域都有可能 ...

  8. java内存溢出分析工具

    http://www.cnblogs.com/preftest/archive/2011/12/08/2281322.html java内存溢出分析工具:jmap使用实战 在一次解决系统tomcat老 ...

  9. jvm内存溢出问题的定位方法

    jvm内存溢出问题的定位方法 今天给大家带来JVM体验之内存溢出问题的定位方法. 废话不多说直接开始: 一.Java堆溢出 测试代码如下: import java.util.*; public cla ...

随机推荐

  1. SAP CRM 用户界面对象类型和设计对象

    在CRM中的用户界面对象类型的帮助下,我们可以做这些工作: 进行不同的视图配置 创建动态导航 从设计层控制字段标签.值帮助 控制BOL对象的属性的可视性 从导航栏访问自定义组件 一个用户界面对象类型之 ...

  2. android SystemServer.java启动的服务。

    EntropyService:熵(shang)服务,用于产生随机数PowerManagerService:电源管理服务ActivityManagerService:最核心服务之一,Activity管理 ...

  3. Android开发案例 – 在AbsListView中使用倒计时

    在App中, 有多种多样的倒计时需求, 比如: 在单View上, 使用倒计时, 如(如图-1) 在ListView(或者GridView)的ItemView上, 使用倒计时(如图-2) 图-1 图-2 ...

  4. CYQ.Data V5 从入门到放弃ORM系列:教程 - Log、SysLogs两个日志类使用

    Log 静态类介绍: Public Static (Shared) Methods GetExceptionMessage 获取异常的内部信息 WriteLogToDB Overloaded. 将日志 ...

  5. 入园记------我的DBA之路

    今天周一拖着疲惫的身躯 11点才离开公司,回到家估计写完这篇博客就要17号了. 一个人走在回家的路上,很黑,突然很多感触,一个人在北京拼搏,不敢停止学习的脚步,因为只要停下来就会感觉到孤独. 回顾一下 ...

  6. 使用Masstransit开发基于消息传递的分布式应用

    Masstransit作为.Net平台下的一款优秀的开源产品却没有得到应有的关注,这段时间有机会阅读了Masstransit的源码,我觉得我有必要普及一下这个框架的使用. 值得一提的是Masstran ...

  7. Team Leader 你不再只是编码, 来炖一锅石头汤吧

    h3{ color: #000; padding: 5px; margin-bottom: 10px; font-weight: bolder; background-color: #ccc; } h ...

  8. 升讯威ADO.NET增强组件(源码):送给喜欢原生ADO.NET的你

    目前我们所接触到的许多项目开发,大多数都应用了 ORM 技术来实现与数据库的交互,ORM 虽然有诸多好处,但是在实际工作中,特别是在大型项目开发中,容易发现 ORM 存在一些缺点,在复杂场景下,反而容 ...

  9. CSharpGL(35)用ViewPort实现类似3DMax那样的把一个场景渲染到4个视口

    CSharpGL(35)用ViewPort实现类似3DMax那样的把一个场景渲染到4个视口 开始 像下面这样的四个视口的功能是很常用的,所以我花了几天时间在CSharpGL中集成了这个功能. 在CSh ...

  10. 关于php语言的使用!

    ------php语言与JavaScript的使用 方法是相似 <script type="text/javascript"> </script>--js与 ...