博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java内存区域及内存溢出
阅读量:7024 次
发布时间:2019-06-28

本文共 4174 字,大约阅读时间需要 13 分钟。

堆溢出

Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径避免垃圾回收,当到达最大堆的容量限制后就会产生Java.lang.OutOfMemoryError.

/** * VM Options: * -Xms20M * -Xmx20M * -XX:+HeapDumpOnOutOfMemoryError */public class HeapOOM{    static class OOMObject{}        public static void main(String[] args){        List
list = new ArrayList
(); while(true){ list.add(new OOMObject()); } }}

结果:

GC多次执行后触发OutOfMemoryError.

image

栈溢出

关于虚拟机栈,在Java规范中描述了两种异常:

  1. 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
  2. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

然而,在单线程下,虚拟机在栈空间不足时会尝试扩展栈空间,因此,当无法继续分配时,到底是内存太小,还是已使用的栈空间太大,其实是一回事。在实验中,单线程环境下,只会抛出StackOverflowError异常。

/** * VM Option: * -Xss160K */public class JavaVMStackSOF{    private int stackLength = 1;    public void stackLeak(){        stackLength++;        stackLeak();    }    public static void main(String[] args) throws Throwable{        JavaVMStackSOF oom = new JavaVMStackSOF();        try{            oom.stackLeak();        }        catch(Throwable e){            System.out.println("Stack length:" + oom.stackLength);            throw e;        }    }}

结果:

image

操作系统分配给每个进程的内存是有限制的,通常为操作系统限制总内存-最大堆容量(Xmx)-最大方法区容量(MaxPermSize)-程序计数器消耗。每个线程分配到的栈容量越大,可以建立的线程数目越小。

/** * VM Options: * -Xss2M */public class JavaVMStackOOM{    private void dontStop(){        while(true){}    }        public void stackLeakByThread(){        while(true){            Thread thread = new Thread(new Runnable(){                @Override                public void run(){                    dontStop();                }            });            thread.start();        }    }        public static void main(String[] args) throws Throwable{        JavaVMStackOOM oom = new JavaVMStackOOM();        oom.stackLeakByThread();    }}

结果:

Exception in thread "main" java.lang.outOfMemoryError: unable to create new native thread

运行时常量池溢出

运行时常量池在JDK 1.6及之前版本中在方法区中,在1.7及之后转移至堆空间。在JDK 1.6及之前版本中可以通过限制方法区大小,从而间接限制运行时常量池大小。

/** * ONLY WORKS BEFORE JDK 1.7 * VM Options: * -XX:PermSize=10M * -XX:MaxPermSize=10Mpublic class RuntimeConstantPoolOOM{    public static void main(String[] args){        List
list = new ArrayList
(); int i = 0; while(true){ list.add(String.valueof(i++).intern()); } }}

结果:

Exception in thread "main" java.lang.OutOfMemoryError:PermGen space

方法区溢出

方法区用于存放Class相关信息,因此要使得方法区溢出,除了在JDK 1.7之前使运行时常量池溢出外,基本的思路是运行时生成大量的类去填满方法区。

结果
Exception in thread "main" java.lang.OutOfMemoryError:PermGen space

直接内存(DirectMemory)溢出

直接内存不是虚拟机运行时数据区的一部分。在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,使用Native函数库直接分配堆外内存。

DirectMemory容量可通过-XX: MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值一样(-Xmx)。直接通过allocateMemory可以造成本机内存溢出。

结果:

Exception in thread "main" java.lang.OutOfMemoryError
直接内存溢出的一个特征是Heap Dump文件中不会看先明显的异常指示。如果OOM之后Dump文件很小,而程序中又直接或间接使用了DIO,就应该检查是否直接内存溢出。

String.intern()

String.intern()是一个Native方法,作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象,否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。

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

在JDK 1.6中,intern()方法会把首次遇到的字符串实例复制到永久代(方法区运行时常量池),返回的是这个永久代中这个字符串实例的引用,而由StringBuilder创建的字符串实例在Java堆上,所以必然不是同一个引用。

在JDK 1.7中,intern()实现不会再复制,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。

小节

内存区域 描述 VM Option 异常
程序计数器
虚拟机栈 存放编译器可知的各种基本类型,对象引用和returnAddress类型 -Xss160K 每个线程的栈大小 StackOverflowError/OutOfMemoryError
Java堆 存放对象实例 -Xms10M 最大值
-Xmx20M 最小值
OutOfMemory: Java heap space
运行时常亮池 存放编译期生成的字面量和符号引用,运行期也能放入常量池(string.intern())。JDK 1.7之前在方法区中,JDK 1.7及之后移至堆中 随方法区或堆设置 OutOfMemoryError
方法区 存储虚拟机加载的类信息、常亮、静态变量、即时编译器编译后的代码等数据,又称为永久代(Permanent Generation) -XX:PermSize=10M 初始值
-XX:MaxPermSize=20M 最大值
OutOfMemoryError: PermGen space
直接内存 在JDK 1.4中加入NIO类,直接分配堆外内存 -XX:MaxDirectMemorySize=10M,
如果不指定默认与-Xmx一样
OutOfMemoryError

转载地址:http://uppxl.baihongyu.com/

你可能感兴趣的文章
centos 分区扩容
查看>>
JBoss EAP 6 monitoring using remoting-jmx and Zabbix
查看>>
邮件服务器
查看>>
OOAD-设计模式-原型模式
查看>>
Java FAQ(2)
查看>>
JavaScript常用事件总结
查看>>
squid在企业网中的应用
查看>>
主元素 Majority Element
查看>>
关于加密的一些笔记
查看>>
MySQL并发处理
查看>>
android Bitmap的内存优化
查看>>
我的友情链接
查看>>
mysql主从备份
查看>>
高并发Web服务的演变—节约系统内存和CPU
查看>>
MyBatis的Batch处理导致秒杀系统超卖的问题
查看>>
远程备份电脑 —— 方案二:不需要使用.bat批运行文件
查看>>
解决Redhat Linux5 yum出现This system is not registered with RHN的方法
查看>>
10.23 linux任务计划cron 10.24 chkconfig工具 10.25 systemd管理服务 10.26 unit介绍 10.27 target介绍...
查看>>
2015/01/26PP租车面试总结
查看>>
荷露叮咚wp建站系列视频课程1.WordPress基本介绍
查看>>