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

Java堆溢出

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

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

 1 public class HeapOOM{
 2   static class HeapOOM{
 3     }
 4    public static void main(String[] args)
 5   {
 6     List<OOMObject> list = new ArrayList<OOMObject>();
 7       While(1)
 8        {
 9            list.add(new OOMObject());
10     }
11 }
12 }

  代码中对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的测试代码:

 1 public class JavaVMStackSOF{
 2     private int stackLength = 1;
 3     public void stackLeak() {
 4         stackLength++;
 5         stackLeak();
 6     }
 7     public static void  main(String[] args) {
 8         JavaVMStackSOF com = new JavaVMStackSOF();
 9         try{
10             com.stackLeak();
11         }
12         catch (Throwable e){
13             System.out.println("Stack Length" + com.stackLength);
14             throw e;
15         }
16     }
17 }

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

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

  解决思路:

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

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

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

  下面上代码:

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

  设置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堆最大值一样。

  下面贴代码: 

 1 public class DirectMemroyOOM{
 2
 3     public static final int _1MB = 1024 * 1024;
 4
 5     public static void main(String[] args) throws Exception{
 6         Field unsafeField = Unsafe.class.getDeclareFields()[0];
 7         unsafeField.setAccessible(true);
 8         Unsafe unsafe = (Unsafe) unsafeField.get(null);
 9         while (1)
10             unsafe.allocateMemory(_1MB);
11     }
12 }

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

  解决思路:

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

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

Java之OutOfMemoryError简单分析相关推荐

  1. java.lang.OutOfMemoryError: GC overhead limit exceeded问题分析及解决

    一.错误重现 2022-12-29 10:12:07.210 ERROR 73511 --- [nio-8001-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServle ...

  2. java.lang.OutOfMemoryError GC overhead limit exceeded原因分析及解决方案

    最近一个上线运行良好的项目出现用户无法登录或者执行某个操作时,有卡顿现象.查看了日志,出现了大量的java.lang.OutOfMemoryError: GC overhead limit excee ...

  3. LeetCode——LCP 29. 乐团站位[简单]——分析及代码(Java)

    LeetCode--LCP 29. 乐团站位[简单]--分析及代码[Java] 一.题目 二.分析及代码 1. 直接计算 (1)思路 (2)代码 (3)结果 三.其他 一.题目 某乐团的演出场地可视作 ...

  4. java电话卡问题_Android 系统(261)—SIM卡不识或者掉卡简单分析

    SIM卡不识或者掉卡简单分析 1. 识卡大体过程 2.不识卡问题 不识卡的主要原因: ①Modem发生错误,异常或者重启. ②没有收到卡回的ATR ③有收到卡回的ATR,但是后面跟卡通信出现异常 ④S ...

  5. java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得

    该文章出自:http://www.cnblogs.com/hucn/p/3572384.html 分析工具:http://www.blogjava.net/jjshcc/archive/2014/03 ...

  6. 深入理解 Java 虚拟机(第一弹) - Java 内存区域透彻分析

    来自:好好学java 这篇文章主要介绍Java内存区域,也是作为Java虚拟机的一些最基本的知识,理解了这些知识之后,才能更好的进行Jvm调优或者更加深入的学习,本来这些知识是晦涩难懂的,所以希望能够 ...

  7. 深入理解Java虚拟机-Java内存区域透彻分析

    Java虚拟机深入理解系列全部文章更新中- 深入理解Java虚拟机-Java内存区域透彻分析 深入理解Java虚拟机-常用vm参数分析 深入理解Java虚拟机-JVM内存分配与回收策略原理,从此告别J ...

  8. java.lang.OutOfMemoryError:GC overhead limit exceeded

    我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性 ...

  9. 应用jacob组件造成的内存溢出解决方案(java.lang.OutOfMemoryError: Java heap space)

    http://www.educity.cn/wenda/351088.html 使用jacob组件造成的内存溢出解决方案(java.lang.OutOfMemoryError: Java heap s ...

最新文章

  1. 【51CTO学院三周年】我的职业生涯有贵人相助--小强老师
  2. pyspark import 可以通过 --py-files
  3. MSU发布2020年度4K编码效率评测结果
  4. java中float%int_java中short、int、long、float、double取值范围
  5. ioc框架 java_从零开始实现一个简易的Java MVC框架(三)--实现IOC
  6. 【WebRTC---源码篇】(二)PeerConnectionFactory
  7. Java——UPD输出及优化再优化
  8. 什么90%的人,自学python都不能学会,原来问题出在这,赶紧看看!
  9. 查看zookeeper二进制日志方式
  10. 已走过8年时间!百度这一重要产品正式宣布停止服务
  11. Python+matplotlib绘制箱线图并设置异常值、中值、均值显示格式
  12. n平方的求和公式_极限求解--数列前n项和公式推导(补充知识)
  13. 数据挖掘与数据分析的区别是什么
  14. 【基础】网络常见的9大命令,非常实用!
  15. CV求职面经 -- 数学基础
  16. PTA程序设计基础6 7-1 列表排序、逆序 (10 分)C语言解法
  17. linux下不能访问windows磁盘
  18. 龙威PS305D维修案例收集
  19. ReactNative实现仿微信或者通讯录快速索引功能
  20. 赛博哈希CyberHash顺势而为,获数千万美金的融资

热门文章

  1. 红米k30pro工程包工厂包
  2. Tomcat 运行 maven项目报错 com.sun.faces.config.ConfigureListener
  3. 不是你不懂黑盒攻击,而是你还不懂SurFree
  4. Ubuntu 14.04重装mysql
  5. 乐鑫ESP32-C3开发(一)简述和目录
  6. python另存为excel_为什么不能从python代码中“另存为”Excel文件?
  7. VBA--LBound函数与UBound函数用法详解
  8. TORCH.JIT理解
  9. 旧文 2012.12.07 关于和赛扶
  10. Linux为什么不怕病毒