如果你做TCP通讯或者map集合操作,并发处理等功能时,很容易出现 Java 内存溢出的问题。本篇文章,带领大家深入jvm,分析并找出jvm内存溢出的代码。

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 xttblog=new LinkedList();//作为GC Root 
while(true){ 
xttblog.add(new HeapOOMTest());//疯狂创建对象 



-Xms20m -Xmx20m作用是将jvm的最小堆容量和最大堆容量都设定为20m,这样就不会动态扩展jvm堆了 
这段代码疯狂的创建对象,虽然对象没有声明变量名引用,但是将对象添加到队列l中,这样队列l就持有了一份对象的引用 
通过可达性算法(jvm判断对象是否可被收集的算法)分析,队列l作为GC Root,每一个对象都是l的一个可达的节点,所以疯狂创建的对象不会被收集,这就是内存泄漏,这样总有一天堆就溢出了。

运行结果:

Exception in thread “main” java.lang.OutOfMemoryError: Javaheap 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的连接 
如果不存在泄漏:

看下是否能增大jvm堆的最大容量

优化程序,减小对象的生命周期

前期准备 
当发生堆溢出的时候,可以让程序在崩溃时产生一份堆内存快照 
产生堆内存快照的方法: 
给jvm加上参数XX:+HeapDumpOnOutofMemoryError,这样就会在程序崩溃的时候,产生一份堆内存快照 
分析堆内存快照我建议用jdk自带的可视化监视工具visualVM,位置在jdk安装目录下的bin,如果是在Linux环境的话,可以把快照传到window。因为分析工具会占用很大的内存,不建议在服务端进行分析。

实战 
下面对刚才程序产生的堆内存快照进行分析。 
打开visualVM,装入刚刚生成的快照,打开类标签页 
visualVM 
队列和疯狂创建的对象几乎占满了整个栈,想要让垃圾收集器回收这些对象,要让他们与GC Root断开连接 
双击HeapOOMTest类,跳转到实例标签页,可以查看这个类的所有实例 
在实例上右键——显示最近的垃圾回收根节点,可以看到这个对象与根节点的连接 
StackOverFlowError 
只要断开HeapOOMTest对象与LinkedList的连接,这些疯狂创建的对象就会被收集了

栈溢出 
调用方法的时候,会在栈中入栈一个栈帧,如果当前栈的容量不足,就会发生栈溢出StackOverFlowError 
那么只要疯狂的调用方法,并且有意的不让栈帧出栈就可以导致栈溢出了。

下面来一次栈溢出










10 
//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 
只要创建足够多的常量,就会发生溢出










10 
11 
12 
13 
14 
15 
16 
/** 
* jvm参数: 
* jdk6以前:-XX:PermSize=10M -XX:MaxPermSize=10M 
* jdk7开始:-Xms10m -Xmx10m 
* */ 
public class RuntimePoolOOM { 
public static void main(String[] args){ 
int i=1; 
//保持常量的引用,防止被fullgc收集 
LinkedList xttblog=new LinkedList(); 
while(true){ 
//将常量添加到常量池 
xttblog.add(String.valueOf(i++).intern()); 



因为jdk6以前,运行时常量池是在方法区(永生代)中的,所以要限制永生代的容量,让内存溢出来的更快。 
从jdk7开始,运行时常量池是在堆中的,那么固定堆的容量就好了 
这里用了链表去保存常量的引用,是因为防止被fullgc清理,因为fullgc会清理掉方法区和老年代 
intern()方法是将常量添加到常量池中去,这样运行时常量池一直都在增长,然后内存溢出

运行结果:

Exception in thread “main” java.lang.OutOfMemoryError: Java heap space

  1. at java.lang.Integer.toString(Unknown Source)
  2. at java.lang.String.valueOf(Unknown Source)
  3. at test.RuntimePoolOOM.main(RuntimePoolOOM.java:30)

提示在heap区域发生内存溢出,果然运行时常量池被移到了堆中

方法区溢出

方法区是存放类的信息,而且很难被gc,只要加载了大量类,就有可能引起方法区溢出 
这里将不做演示了,想试试的可以用cglib创建大量的代理类

分析

工作中也有可能会遇上方法区溢出: 
当多个项目都有相同jar包的时候,又都存放在WEB-INF\lib\下,这样每个项目都会加载一遍jar包。会导致方法区中有大量相同类(被不同的类加载器所加载),又不会被gc掉。

解决方案

在应用服务器中建立一个共享lib库,把项目中常用重复的jar包存放在这里,项目从这里加载jar包,这样就会大大减少类加载的数量,方法区也“瘦身”了
如果实在不能瘦身类的话,那可以扩大方法区的容量,给jvm指定参数-XX:MaxPermSize=xxxM

jvm 内存溢出问题排查方法的更多相关文章

  1. JVM内存溢出环境备份方法

    线上Tomcat服务内存溢出,且不容易重现,又没配置JMX监控端口,如何在不重启Tomcat的情况下备份堆dump和线程dump,进而分析原因? 因为Tomcat以服务模式运行,直接用JVisualV ...

  2. JVM - 内存溢出问题排查相关命令jcmd jmap

    jcmd http://docs.oracle.com/javase/8/docs/technotes/tools/windows/jcmd.html jcmd-l  列出正在执行的JAVA进程ID ...

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

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

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

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

  5. jvm内存溢出分析

    概述 jvm中除了程序计数器,其他的区域都有可能会发生内存溢出 内存溢出是什么? 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出 内存溢出和 ...

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

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

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

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

  8. Java常见的几种内存溢出及解决方法

    Java常见的几种内存溢出及解决方法[情况一]:java.lang.OutOfMemoryError:Javaheapspace:这种是java堆内存不够,一个原因是真不够(如递归的层数太多等),另一 ...

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

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

随机推荐

  1. 微信小程序制作家庭记账本之二

    第二天,继续学习制作记账本,网上搜寻别人的源码进行学习,但是搜寻过程中总是能看到github这个东西,不清楚这是什么东西,明天继续努力吧.

  2. 环绕声5.1ch

    简单说5.1ch就是数字影院中的音频输出术语,环绕立体声输出,让人有置身电影院的感觉,由五个音箱(两个主音箱.两个环绕箱.一个中置箱)+一个低音炮组成 5.1环绕声包括了5个全频带声道和 1个低频效果 ...

  3. git从安装到使用

    一.Git简介 Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制 ...

  4. 怎样从外网访问内网Tomcat?

    本地安装了一个Tomcat,只能在局域网内访问,怎样从外网也能访问到本地的Tomcat呢?本文将介绍具体的实现步骤. 准备工作 安装并启动Tomcat 默认安装的Tomcat端口是8080. 实现步骤 ...

  5. javaweb笔记09—(session会话及验证码问题)

    第一部分+++++++++++1.session会话 定义:session会话——对某个web应用程序的一次整体访问的过程. 由来:无连接的http协议是无状态的,不能保存每个客户端私有信息 a用户和 ...

  6. selenium webdriver 实现Canvas画布自动化测试

    https://blog.csdn.net/xiaoguanyusb/article/details/80324210 由借鉴意义, 转过来 canvas 是一个画布,定位元素时只能定位到画布上,如下 ...

  7. OL6.3 设置本地yum源

    仅在 Oracle Linux Server release 6.3 上测试 PS:Oracle Linux Server release 6.3仅用于测试,不能用于商业用途 [root@oracle ...

  8. yield表达式形式

    首先了解 1.iterator iterator叫做迭代器,用来遍历可以序列化的数据,比如一个list,set 等,当然如果对象想要能够使用迭代器来遍历,只要在该对象的类中添加__iter__()方法 ...

  9. mybatis generator自动生成sqlmap代码的不完善之处以及解决方法

    a) 建表时,字段名称建议用"_"分隔多个单词,比如:AWB_NO.REC_ID...,这样生成的entity,属性名称就会变成漂亮的驼峰命名,即:awbNo.recId b)or ...

  10. 'root'@'127.0.0.1'没有grant privileges

    从另外一台服务器拷贝了个mysql实例过来,给root@'%'授权的时候提示ERROR 1045 (28000): Access denied for user 'root'@'localhost' ...