您的位置:首页 > 编程语言 > Java开发

Java 内存区域与内存溢出异常

2015-03-14 10:09 211 查看


一:运行时数据区域

1:程序计数器-线程私有

在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,称为“线程私有”的内存,是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

2: Java虚拟机栈(常说的栈)-线程私有

虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等。

局部变量表:基本数据类型,对象引用、returnAddress类型。long 和double占据两个局部变量空间,其余只有一个。

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

3:本地方法栈

本地方法栈则为虚拟机使用到的Native方法服务。

4:堆-线程共享

所有的对象实例以及数组都要在堆上分配。

5:方法区

用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

二:对像创建

1:虚拟机在遇到一条new 指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

2:分配内存

指针碰撞:所用用过的内存放在一边,没用过的放在另一边

空闲列表:用过的和没用过的放在一起

避免内存分配冲突-本地线程分配缓冲(每个线程预先分配一块内存)

3:将分配的内存空间初始化为零值

4:虚拟机对对象进行必要的设置。调用init方法

三:对象的内存布局

对象头:

两部分信息:第一部分用于存储对象自身的运行时数据(32bit 或64bit),另一部分是类型指针,即对象指向它的类元数据的指针。如果对象是Java数组,那在对象头中必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小。

实例数据

对齐填充

四:访问对象

1:通过句柄访问对象

2:通过指针直接访问





五:OutOfMemoryError异常

1:Java 堆溢出

通过设置参数-Xms20m(设置堆得最小值) -Xmx20m(堆的最大值) -XX:+HeapDumpOnOutOfMemoryError可以让虚拟机出现溢出异常时Dump出当前的内存堆转储快照。

public class HeapOOM {
    static class OOMObject {
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List<OOMObject> list = new ArrayList<HeapOOM.OOMObject>();
        while (true) {
            list.add(new OOMObject());
        }
    }

}
运行结果:

java.lang.OutOfMemoryError: Java heap space

Dumping heap to java_pid5213.hprof ...
Heap dump file created [27617151 bytes in 0.167 secs]

2:虚拟基栈和本地方法栈溢出(通过调整-Xss参数设置-Xss128k)
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

3:方法区和运行时常量池异常

通过-XX:PermSize和-XX:MaxPermSize限制方法区大小

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

如果异常,抛出PermGen space
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: