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. HTTPS与SSL(一)

    1.  HTTPS HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版 ...

  2. IIS 处理高并发

    1. 调整IIS 7应用程序池队列长度 由原来的默认1000改为65535. IIS Manager > ApplicationPools > Advanced Settings Queu ...

  3. [Java]在xp系统下java调用wmic命令获取窗口返回信息无反应(阻塞)的解决方案

    背景:本人写了一段java代码,调用cmd命令“wmic ...”来获取系统cpu.mem.handle等资源信息.在win7操作系统下运行没有问题,在xp系统下却发现读取窗口反馈信息时无反应(阻塞) ...

  4. SQL 语句 使用附加和分离

    use mastergo declare @flg int --返回0表示成功 否则表示失败declare @msg varchar(50) --显示成功或失败的消息declare @dbname v ...

  5. hihocoder 1080 线段树(区间更新)

    题目链接:http://hihocoder.com/problemset/problem/1080 , 两种操作的线段树(区间更新). 这道题前一段时间一直卡着我,当时也是基础不扎实做不出来,今天又想 ...

  6. 标准IO ——将A文件fpd第3个字节之后的内容复制到文件fps

    /* *使用标准IO ——将A文件fpd第3个字节之后的内容复制到文件fps 流程: 1.创建两个流,链接目标文件和源文件 2.输入流的基准点偏移四个单位然后输入缓冲区 3.输出流读取缓冲区数据送入文 ...

  7. 最简单的基于FFMPEG的转码程序 —— 分析

    模块:  libavcodec    - 编码解码器         libavdevice   - 输入输出设备的支持         libavfilter   - 视音频滤镜支持         ...

  8. SAP云平台运行环境Cloud Foundry和Neo的区别

    SAP云平台提供了两套运行环境:Cloud Foundry和Neo 从下图能发现,Cloud Foundry的运行环境,基础设施由第三方公司提供,比如Amazon亚马逊和Microsoft微软,SAP ...

  9. SSM框架快速搭建

    1.   新建Maven项目 ssm 2.    pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xml ...

  10. JQuery根据关键字检索html元素并筛选显示

    背景:标题比较唬人,实际上就是在文本框中输入关键字,通过关键字检索html元素,筛选后显示在界面上. Html元素如下: <div> <input type="text&q ...