Java之OutOfMemoryError简单分析

  最近编码遇到了Java内存溢出的问题,所以就想顺便总结一下几种导致Java内存溢出的栗子,以及碰到Java内存溢出要如何去解决。

Java堆溢出

  Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达的路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆容量限制之后就会产生内存溢出的异常。

  下面通过一段代码进行堆内存溢出异常的测试。 

 public class HeapOOM{
static class HeapOOM{
}
public static void main(String[] args)
{
List<OOMObject> list = new ArrayList<OOMObject>();
While(1)
{
list.add(new OOMObject());
}
}
}

  代码中对Java堆的限制大小是20M,不可扩展。具体设置是在VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError。

  当有堆溢出异常时,在异常堆栈信息栏中会出现“java.lang.OutOfMemoryError”,然后会提示:“Java heap space”。

  解决思路:

  一般是通过内存映象分析工具对Dump出来的堆转储快照进行分析,重点是确认内存中对象是否是必要的,也就是先分清楚是Memory Leak(内存泄露)还是Memory OverFlow(内存溢出)。如果是内存泄露,可以通过工具检查泄露对象到GC Roots的引用链。然后就能进而定位出泄露代码的位置。

  如果不是内存泄露,也就是说内存中的对象都存活。那就检查虚机的参数,与物理机进行对比,看看是否可以调大,从代码检查是否存在生命周期过长的对象,尝试减少程序运行期间内存的消耗。

虚拟机栈和本地方法栈溢出

  由于在HotSpot虚拟机中并不区分虚拟机栈以及本地方法栈,因此虽然-Xoss(用于设置本地方法栈的大小)参数存在,但是实际上是无效的。栈容量只由-Xss参数设定。

  下面是虚拟机和本地方法栈OOM的测试代码:

 public class JavaVMStackSOF{
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) {
JavaVMStackSOF com = new JavaVMStackSOF();
try{
com.stackLeak();
}
catch (Throwable e){
System.out.println("Stack Length" + com.stackLength);
throw e;
}
}
}

  上述代码使用了-Xss=128k,减少了栈内存的容量,结果抛出StackOverflowError异常,异常出现时输出的堆栈深度相应的缩小。

  定义了大量的本地变量,增大此方法帧中本地变量表的长度,结果抛出StackOverflowError异常时输出的堆栈深度相应缩小。也就是说在单个线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配时,都会抛出StackOverflowError异常。

  解决思路:

  如果使用虚拟机默认参数,栈深度在大多数情况下达到1000-2000没问题,正常的方法调用(含递归),这个深度完全够用,如果是建立多线程导致内存溢出,在不能减少线程数情况下,只能通过减少最大堆和减少栈容量来换取更多的线程。

方法区以及运行时常量池溢出

  由于运行时常量池是方法区一部分,所以就放在一起了。

  下面上代码:

 public class RuntimeConstantPoolOOM{
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
int i =0;
while(1)
list.add(String.valueOf(i++).intern());
}
}

  设置VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M。String.intern()是一个Native方法,它的作用是:如果字符串常量池已经包含了等于此String对象的字符串,则返回代表池中这个字符串的String对象:否则,将此String包含的字符串添加到常量池中,并且返回此String的引用。从结果中可以看到,在OutOfMemoryError后跟随的提示信息是:PermGen Space,说明运行时常量池属于方法区一部分。

  解决思路:

  方法区溢出是一种常见的内存溢出异常,一个类要被垃圾收集器回收掉,判断条件是比较苛刻的。在经常动态生成大量的Class应用中,需要特别注意类的回收状况,常见的场景有:大量jsp或动态生成jsp文件的应用、基于OSGI的应用以及使用GGLib字节码增强(当前许多主流框架Spring、Hibernate都会用到GGLib这类字节码技术)和动态语言。

本机直接内存溢出

  DirectMemory容量可以通过-XX:MaxDirectMemorySize指定,如不指定,默认与java堆最大值一样。

  下面贴代码: 

 public class DirectMemroyOOM{

     public static final int _1MB = 1024 * 1024;

     public static void main(String[] args) throws Exception{
Field unsafeField = Unsafe.class.getDeclareFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (1)
unsafe.allocateMemory(_1MB);
}
}

  指定VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M,代码越过了DirectByBuffer类,直接通过反射获取Unsafe实例进行内存分配,之所以这么做是因为虽然使用DirectByteBuffer分配内存也会抛出内存溢出异常,但是抛出的异常并没有真正向操作系统申请分配内存,而是通过计算得知内存无法分配,于是手动抛出异常,真正申请内存的方法是unsafe.allocateMemory()。

  解决思路:

  由于此种内存溢出一个明显的特征是在Heap Dump文件中不会看见明显的异常,如果发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,那就可以考虑一下是不是本机内存直接溢出了。

  以上就是有关java内存溢出相关的总结了,如果有不对的地方,欢迎指出,大家一起交流。

  

  PS:本博客欢迎转发,但请注明博客地址及作者~

   博客地址:http://www.cnblogs.com/voidy/

   <。)#)))≦ 

Java之OutOfMemoryError简单分析的更多相关文章

  1. 各排序算法的Java实现及简单分析

    一,直接插入排序 //直接插入排序的算法时间复杂度分析: //如果输入为正序,则每次比较一次就可以找到元素最终位置,复杂度为O(n) //如果输入为反序,则每次要比较i个元素,复杂度为O(n2) // ...

  2. java构造器级简单内存分析

    java构造器的使用(基础篇) 构造方法也叫构造器,是创建对象时执行的特殊方法,一般用于初始化新对象的属性. 基本定义语法: 访问控制符 构造方法名([参数列表]){ 方法体 } 注:"访问 ...

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

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

  4. 透过byte数组简单分析Java序列化、Kryo、ProtoBuf序列化

    序列化在高性能网络编程.分布式系统开发中是举足轻重的之前有用过Java序列化.ProtocolBuffer等,在这篇文章这里中简单分析序列化后的byte数组观察各种序列化的差异与性能,这里主要分析Ja ...

  5. 简单分析Java的HashMap.entrySet()的实现

    关于Java的HashMap.entrySet(),文档是这样描述的:这个方法返回一个Set,这个Set是HashMap的视图,对Map的操作会在Set上反映出来,反过来也是.原文是 Returns ...

  6. java 中 “文件” 和 “流” 的简单分析

    java 中 FIle 和 流的简单分析 File类 简单File 常用方法 创建一个File 对象,检验文件是否存在,若不存在就创建,然后对File的类的这部分操作进行演示,如文件的名称.大小等 / ...

  7. Java虚拟机三:OutOfMemoryError异常分析

    根据Java虚拟机规范,虚拟机内存中除过程序计数器之外的运行时数据区域都会发生OutOfMemoryError(OOM),本文将通过实际例子验证分析各个数据区域OOM的情况.为了更贴近生产,本次所有例 ...

  8. java基础---->hashSet的简单分析(一)

    对于HashSet而言,它是基于HashMap实现的,底层采用HashMap来保存元素的.今天我们就简单的分析一下它的实现.人生,总会有不期而遇的温暖,和生生不息的希望. HashSet的简单分析 一 ...

  9. java:java静态代理与动态代理简单分析

    java静态代理与动态代理简单分析 转载自:http://www.cnblogs.com/V1haoge/p/5860749.html 1.动态代理(Dynamic Proxy) 代理分为静态代理和动 ...

随机推荐

  1. GoDaddy虚拟主机创建FTP 图文流程

    有了ftp各种操作就方便多了,也不用通过网页的控制面板来修改代码了 狗爹linux虚拟主机创建FTP 1. 通过虚拟主机管理界面,进入cPanel控制面板 2. 进入FTP管理页面 3. 填写账号.密 ...

  2. Java有关List的stream基本操作

    参考博客: https://www.jianshu.com/p/9fe8632d0bc2 Stream简介 Java 8引入了全新的Stream API.这里的Stream和I/O流不同,它更像具有I ...

  3. 【extjs6学习笔记】1.11 初始: config

    Ext JS有一个名为config的功能. 该配置允许您使用默认值声明公共属性,这些属性将被其他类成员完全封装. 通过config声明的属性将自动获取get()和set()方法,如果类没有定义这些方法 ...

  4. sql server 2012不能全部用到CPU的逻辑核心数的问题

    最近在工作中遇到了,sql server 2012 不能把CPU 的核心全部用到的问题.通过分析工具看到 总共CPU核心有72核,但sql 只能用到40核心,想信也有很多人遇到这问题,那么今天这节就先 ...

  5. linux 命令——43 killall(转)

    Linux 系统中的killall命令用于杀死指定名字的进程(kill processes by name).我们可以使用kill命令杀死指定进程PID的进 程,如果要找到我们需要杀死的进程,我们还需 ...

  6. POJ-2226 Muddy Fields---二分图匹配+巧妙构图

    题目链接: https://vjudge.net/problem/POJ-2226 题目大意: 用宽度为1长度不限的木板将水洼‘*’盖住而不盖住草‘.' Sample Input 4 4 *.*. . ...

  7. 运行自己的shell脚本

    shell脚本可以直接./**.sh,也可以bash **.sh 我用./**.sh运行自己写的一个脚本,会出现如下的错误: bnrc@bnrc:~$ ./pixel.sh bash: ./pixel ...

  8. gitlab系列详解

    虚拟机的安装1.安装virtualboxhttps://www.virtualbox.org/2.安装centos6.63.配置网络右键-->网络-->网卡2-->host-only ...

  9. 51nod——2476 小b和序列(预处理 思维)

    对于每一个元素,预处理出它作为最小值,两边可以作用到的最大位置.比如下标∈[0,8]的这个数组:1 8 6 2 5 4 3 8 7,1可以作用到所有区间,2可以作用到区间[1,8],第一个8可以作用到 ...

  10. 一步一步讲解安装NodeJs开发环境

    node.js简介 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效. Node. ...