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

Java内存区域介绍与Java内存溢出异常分析

2015-06-03 00:37 1501 查看
本博客是在读《深入理解Java虚拟机》这本书第二章的一个笔记。

首先介绍一下Java执行程序时的内存区域

如图,我们将内存分成了5大块区域:方法区,堆,虚拟机栈,本地方法栈以及程序计数器。

这几个区域的功能概括如下:

程序计数器:占用内存很小,作用就是指示下一条将要执行指令的地址,分支,循环,跳转,异常处理以及线程恢复等都需要计数器来完成,而且他是线程独立的(每个线程拥有一个独立的程序计数器)。

Java虚拟机栈:虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的时候都会创建一个栈帧(栈帧是存储局部变量表,操作数栈,动态链接,方法出口等信息),这个也是线程独立的。

本地方法栈:与虚拟机栈类似,只不过本地方法栈中执行的是Native方法,在SUN HotSpot虚拟机中本地方法栈与Java虚拟机栈合二为一了。

Java堆,一般Java堆是内存中最大的一块,他是线程共享的,而且可以处于物理上不连续的内存空间,这块内存的唯一作用是存储对象实例,几乎所有的对象实例都在这分配内存,这里也是垃圾收集器管理的主要区域。

方法区:方法区是线程共享的,而且也可以处于物理上不连续的内存空间里,和java堆相似,不过他存储的是已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

运行时常量池:运行时常量池是是方法区的一部分,会存放Class文件中的常量池信息。

直接内存:直接内存不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,他会受到本机内存大小和处理器寻址空间的限制。

然后我们介绍一下HotSpot虚拟机对象的 一些信息:

对象的创建过程:检查(执行)类加载->分配内存->初始化为0->对对象进行设置->执行<init>方法(此部程序员完成)
其中分配内存又分为指针碰撞和空闲列表分配,在这不细说了,有兴趣可以查阅相关资料。
对象的内存布局
分为三部分:对象头、实例数据、对齐填充。
对象头分为两部分为存储对象自身的运行时数据(比如哈希表、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等),另一部分为类型指针,是对象指向它的类元数据的指针。
实例数据是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。
对齐填充仅仅起着占位符的作用,因为HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍。
对象的访问定位
使用句柄访问和直接指针访问。

除了程序计数器外,虚拟机内存的其他几个运行时区域都有可能发生OutOfMemoryError异常的可能。当栈帧太大时,虚拟机栈和本地方法栈有可能抛出StackOverfloatError。

测试方法可以使用Eclipse的插件Eclipse Memory Analyzer来进行测试。

Eclipse Memory Analyzer的安装与使用:http://blog.sina.com.cn/s/blog_5fc933730101g0in.html

可以设置运行时虚拟机de参数:

-Xms 堆的最小值 -Xmx堆的最大值
-XX:+HeapDumpOnOutOfMemoryError让虚拟机在出现内存溢出时Dump出当前的内存堆转储快照。

-Xoss本地方法栈的大小 -Xss栈容量
-XX:PermSize常量池的大小 -XX:MaxPermSize常量池的最大容量
-XX:MaxDirectMemorySize直接内存容量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: