此篇是上一篇文章Java内存溢出异常(上)的续篇,没有看过的同学,可以先看一下上篇。本篇文章将介绍剩余的两个溢出异常:方法区和运行时常量池溢出。

方法区和运行时常量池溢出

这部分为什么会放在一起呢?在Java内存区域与内存溢出异常这篇文章中我们说过,运行时常量池实际上属于方法区的一部分,所以就放在一起讨论。

常量池溢出

在讨论常量池的溢出之前,先说明一下String.intern()方法,该方法会检查字符串常量池中是否已经包含了一个等于此String对象的字符串,如果已经包含了,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。看过fastjson源代码的同学应该知道,该方法在fastjson这个json解析库中大量的出现,以提高json的解析速度。

在JDK 1.6之前的版本中,常量池是分配在永久代的,可以通过-XX:PermSize和-XX:MaxPermSize参数来设置大小,从而间接限制其中常量池的容量。

通过以上条件,我们可以轻易的复现这个异常,代码如下:

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

  

如果你的JDK环境是1.6版本之前的,你会得到如下运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen spaceat java.lang.String.intern(Native Method)......

  

如果你是JDK 1.7+,那么这段代码会无限的运行下去。因为在JDK 1.7之后对String.intern()方法进行了修改。

继续看下面这段经典的代码:

public class RuntimeConstantPoolOOM {public static void main(String[] args) {String str1 = new StringBuilder("计算机").append("软件").toString();System.out.println(str1.intern() == str1);String str2 = new StringBuilder("ja").append("va").toString();System.out.println(str2.intern() == str2);}
}

  

这段代码在JDK 1.6中运行,会得到两个false,而在JDK 1.7中运行,会得到一个true和一个false。

出现差异的原因是因为,在JDK 1.6中,intern()方法会把首次遇到的字符串实例复制到常量池中,返回的也是常量池中这个字符串实例的引用,而由StringBuilder创建的字符串实例在Java堆上,所以必然不是同一个引用,将返回false。而JDK 1.7中的intern()实训不会再复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。对str2比较返回false是因为“java”这个字符串在执行StringBuilder.toString()之前已经出现过了,字符串中常量池中已经有它的引用了,不符合“首次”出现的原则。

这就可以解释,为什么在JDK 1.7+版本之后不能用String.intern()方法使常量池溢出的原因了,intern()不会像JDK 1.6之前的版本一样无限复制实例到常量池中了。

方法区溢出

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。这部分的测试可以通过生成大量的类去填满方法区,直到溢出,可以借助CGLib直接操作字节码运行时生成大量的动态类,代码如下:

public class JavaMethodAreaOOM {public static void main(final String[] args) {while (true) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OOMObject.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {return methodProxy.invokeSuper(o, objects);}});enhancer.create();}}static class OOMObject {}
}

  

运行结果如下:

Caused by: java.lang.OutOfMemoryError: PermGen spaceat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)at java.lang.ClassLoader.defineClass(ClassLoader.java:616)...

  

在经常动态生成大量Class的应用中,比如说使用CGLib这类字节码技术的时候,容易出现方法区溢出。所以说在使用这类技术编写框架时,要留意这方面导致的内存溢出。

本机直接内存溢出

本机直接内存的溢出主要与大量的直接操作内存的API有关,比如说NIO相关的API,也可以通过rt.jar中的类使用Unsafe的功能来复现这个异常。DirectMemory容量可通过-XX:MaxDirectMemorySize指定,主要代码如下:

public class DirectMemoryOOM {private static final int _1MB = 1024 * 1024;public static void main(String[] args) throws Exception {Field unsafeField = Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);while (true) {unsafe.allocateMemory(_1MB);}}
}

  

运行结果如下:

Exception in thread "main" java.lang.OutOfMemoryErrorat sun.misc.Unsafe.allocateMemory(Native Method)...

  

由DirectMemory导致的内存溢出,不会在Heap Dump文件中不会看见明显的异常,如果发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,那就可以考虑检查一下是不是这方面的原因。

转载于:https://www.cnblogs.com/qianpangzi/p/10569814.html

Java内存溢出异常(下)相关推荐

  1. JVM-03内存区域与内存溢出异常(下)【OutOfMemoryError案例】

    文章目录 思维导图 Java堆溢出 前置操作 测试类 结果 使用mat分析 内存泄露Memory Leak 内存溢出Memory Overflow 虚拟机栈和本地方法栈溢出 概述 StackOverf ...

  2. Java内存溢出异常及其处理

        熟悉Java内存划分及运行的首要目的就是预防JVM抛出内存溢出相关的异常,或者说当发生这样异常是该如何排查问题,定位问题并且给出合理的解决方案,这对于开发工作以及后期维护工作的顺利进行尤为重要 ...

  3. JAVA内存溢出异常测试

    本文章为看<深入理解JAVA虚拟机>的笔记 JAVA堆异常溢出: /*** Vm args:-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError* ...

  4. java 二维数组内存溢出_程序员:学习心得,Java内存区域,内存溢出异常

    Java 内存区域 Heap 线程公有 存放实例对象 是GC主要管理区域,因此可以更细致的划分为:新生代.老年代 再细致一点划分:Eden区.From Survivor区.To Survivor区 内 ...

  5. java 二维数组内存溢出_模拟Java内存溢出

    本文通过修改虚拟机启动参数,来剖析常见的java内存溢出异常(基于jdk1.8). 修改虚拟机启动参数 这里我们使用的是IDEA集成开发环境,选择Run/Debug Configurations 然后 ...

  6. java内存区域与内存溢出异常_Java内存区域与内存溢出异常

    Java的内存管理是一个老生常谈的问题,虽然Java号称可以自动管理自己的内存,使程序员从内存管理的围墙解放出来,但是一连串的内存泄漏和溢出方面的问题,使得我们不得不去深入了解Java的内存管理机制. ...

  7. JVM最佳学习笔记一---Java内存区域与内存溢出异常

    2019独角兽企业重金招聘Python工程师标准>>> 前言 本笔记参照了周志明<深入理解Java虚拟机:JVM高级特性与最佳实践>第三版,读完之后受益匪浅,让我对Jav ...

  8. Java常见内存溢出异常分析

    Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆内存分为了三部分,新生代,老年代,持久带,其中持久带实现了规范中规定的方法区,而 ...

  9. 写java线程导致电脑内存不足_如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码...

    程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...

最新文章

  1. 13个Pandas奇技淫巧
  2. Windows下Libvirt Java API使用教程(二)- 接口使用说明
  3. 命令测试post_性能测试脚本编写之三
  4. python学习——把计算GC含量的代码封装成函数
  5. Java中HashMap、LinkedHashMap和TreeMap区别使用场景
  6. J.U.C并发框架源码阅读(十七)ReentrantReadWriteLock
  7. CRM客户管理系统能为企业带来什么好处?
  8. Python3实现简单的http server
  9. 牛客网 2018校招真题 吉比特 直线上的点
  10. python 单词拆音节_基于Trie树进行拆分字符串变成拼音音节(一):构建拼音音节模型...
  11. Mysql根据经纬度查询半径多少以内的数据,画个圈圈查数据库
  12. 源码自动生成流程图软件介绍
  13. 字跳三轮面试完,我想去读个硕士了T_T
  14. 【工大SCIR】AAAI20 基于Goal(话题)的开放域多轮对话规划
  15. 菜鸟的三遍读书法进阶
  16. [翻译]WP7 QuickStart-第十一篇-在后台运行程序(墓碑效应)
  17. “金链熊“已致200多家机构受害,或为年度最严重APT攻击事件
  18. Unity3d实现扭动挤压浏览效果
  19. 171023—各进制数输出:二进制转换用格式控制符输出八,十,十六进制数
  20. 深入理解计算机系统_3e 第二章家庭作业 CS:APP3e chapter 2 homework

热门文章

  1. 编程心得体会_CimatronE14高级五轴第三步,平行于曲线铣,会3轴编程更易理解
  2. 代码回到之前版本_聊一聊版本控制
  3. bz2解压命令_Linux文件操作之文件压缩与解压缩命令详解
  4. python中sorted()函数的用法_Python中的Sorted()函数
  5. c语言 在txt文件中搜索关键词_c语言从文件中查找字符串
  6. 获取今日、本周、本月至今日的所有日期
  7. 华南师范大学:迈向智慧校园的“极简”之路
  8. mysql 配置详解
  9. linux图片处理工具GraphicsMagick安装使用
  10. 获取linux samba的文件访问日志