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. 使用纯css实现波浪效果

    有时候我们需要实现水晃动的效果,其实我们可以通过css旋转动画和圆角来实现. 首先来2个div,外层div相对定位,内层div绝对定位,内层div大致位于外层div上半部分.外层div设置一个颜色较深 ...

  2. C++ 强制类型转换(转载)

    转载自:http://www.weixueyuan.net/view/6329.html 在C++语言中新增了四个关键字static_cast.const_cast.reinterpret_cast和 ...

  3. echo -e的用法

    root@bt:~# echo -e "HEAD /HTTP/1.0\n\n"HEAD /HTTP/1.0 root@bt:~# echo -e "HEAD /HTTP/ ...

  4. jquery mmgrid使用

    参考 http://miemiedev.github.io/mmGrid/examples/index.html

  5. JavaScript: apply , call 方法

    我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这里我做如下笔记,希望和大家 ...

  6. 前端知识体系之CSS及其预处理器SASS/LESS

    如果你是个前端设计师,很多时候我们都在写CSS,CSS是定义页面样式的脚本,并不是一种编程语言,只是一行行单纯的描述页面元素的样子,如果对英语熟练的话,它像说话一样简单,这里举个简单的例子: body ...

  7. 漫谈 Clustering (番外篇): Dimensionality Reduction

    由于总是有各种各样的杂事,这个系列的文章竟然一下子拖了好几个月,(实际上其他的日志我也写得比较少),现在决定还是先把这篇降维的日志写完.我甚至都以及忘记了在这个系列中之前有没有讲过“特征”(featu ...

  8. Github的基本功能

    著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:Fadeoc Khaos链接:http://www.zhihu.com/question/20070065/answer/30 ...

  9. 【luogu P3609 [USACO17JAN]Hoof, Paper, Scissor蹄子剪刀布】 题解

    题目链接:https://www.luogu.org/problemnew/show/P3609 ### 看着标签什么记搜什么暴力点进来,读完题第一直觉DP? 还真是个\(DP\). 题目所描述的状态 ...

  10. ssh: connect to host localhost port 22: Connection refused

    1.hadoop安装好之后,执行ssh localhost无法执行, 提示“ssh: connect to host localhost port 22: Connection refused”. 2 ...