深入理解java虚拟机-第一章:java虚拟机内存结构
1.结构分布
2.每个区域解析
--------------割---------------
以下为****上编辑的,版本很难看,上面的Excel整理版。
模块名称 | 线程 | 作用 | 存储对象 | 备注 |
程序计数器 | 私有 | 当前线程所执行字节码的行号指示器, 通过改变这个计数器的值来选取下一条将要执行的字节码指令。 |
数字 | 每个线程都会有一个,线程之间互不影响,独立存储。 |
堆 | 共享 | 垃圾收集器管理的主要区域; 可以处于物理上不连续而逻辑上连续的内存空间中。 |
对象实例 |
内存中最大一块; 可扩展; -Xms参数设置初始值 -Xmx参数设置最大值 内存不够分配实例存储空间时抛出OutOfMemoryError异常 |
虚拟机栈 | 私有 |
描述的是java方法执行的内存模型:每个方法在执行的同时多会创建一个栈帧用于存储局部变量表、操作数栈、动态链表、方法出口等信息。 每一个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。 局部变量表:存放了编译期可指的基本类型(8大类型)、对象引用(引用指针,句柄)、returnAddress类型。 |
生命周期与线程相同; 如果线程请求的栈深度大于虚拟机允许的深度,会报StackOverFlowError; 如果虚拟机栈扩展无法获取做够的内存空间时,会报OutOfMemoryError。 |
|
本地方法栈 | 为虚拟机用到的native方法服务。 |
|||
方法区 | 共享 | 别名:永久代。 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据; |
||
运行时常量池 | 是方法区的一部分; | 编译期生成的字面量和符号引用 |
3.对象访问
java最简单的对象访问,也会涉及到java栈、java堆、方法区三个重要的部分。
以Object object = new Object();为例:
Object object 这部分语义将反映到java栈中的本地变量表中,即reference引用。
new Object() 这部分语义反映在java堆中,形成了存储Object类型所有数据值的结构化内存。注:在java堆中还必须存储有能找到此对象类型数据(对象类型、父类、实现接口、方法),这些类型数据存储在方法区中。
引用访问java堆中对象具体位置的方式有两种:句柄和直接指针。
a)句柄
使用句柄方式访问对象,则会在java堆中划分出一块内存作为句柄池,引用中存储的是对象的句柄地址;
句柄中存储了对象实例地址和类型数据的地址。
优点:引用中直接存储的是稳定的句柄地址,在对象被移动的情况下不需要改变引用,而是改变句柄中的实例数据指针。
b)直接指针
使用直接指针的方式,在java栈中的引用将直接存储对象地址;但是在java堆的布局中就必须考虑如何设置访问类型数据的相关信息。
优点:访问速度快,相比较于句柄访问对象的方式而言,节省了一次指针定位的时间开销。
4.java堆溢出和栈溢出
a.堆溢出抛出 java.lang.OutOfMemoryError。
可能原因:
1)内存泄露。垃圾回收器不能自动回收。
2)实际所需内存值过大,或者对象生命周期过长或者持有时间过长。
解决办法:
java堆参数设置:-xms 堆最小值;-xmx 堆最大值 。
b.栈溢出
解决办法:
栈对应的设置参数为:-Xss 栈容量。
c.运行时常量池溢出
因为运行时常量池分配在方法区内,我们可以通过 -XX:PermSize 和 -xx:ManPermSize来限制方法区的大小来解决。
d.本地直接内存溢出
通过 -XX:MaxDirectMemorySize 来设定。