java内存模型

jdk1.8之后,hotspot的jvm改动:

  1. 使用内存中的元空间代替原先的方法区.

  2. 字符串常量池放进堆中.

程序计数器:

  1. 一块很小的内存区域,主要作用是当前线程所执行字节码的行号指示器.字节码解释工作是,根据改变程序计数器的值来选取下一条所执行的字节码指令.例如:分支,循环,跳转,异常处理.因为jvm多线程是通过线程轮流切换来实现的,因此程序计数器是线程私有的.

  2. 如果是正在执行java方法,此区域存储的是正在执行的虚拟机的字节码指令.如果是native方法,此区域为undefined.程序计数器是唯一一片不会出现outofmemoryerror的区域.

java虚拟机栈

  1. 描述的是方法的执行的内存模型,每一个方法的执行都会创建一个栈桢,用于存储局部变量表,操作数栈,动态连接,方法出口信息.

  2. 局部变量表

    ​ 存储着编译期可知的基本数据类型,对象引用.基本单位是32个字节的slot,long和double会用2个slot存储,因此不具有原子性,但是由于是线程私有的,所以不具有安全问题.所需的空间在编译期就可以确定.

  3. 操作数栈

    一个先进后出的栈,主要处理运算操作.

  4. 动态链接,存储着指向运行时常量池当前栈桢所属方法的引用。

执行过程:

int a= 1;
int b= 2;
int count = a+b;

① 压入 1.

② 压入 2.

③ 执行iadd命令,将栈顶的2个元素相加,即1+2.

本地方法栈

和虚拟机栈的区别是:

虚拟机栈是为java方法提供服务,而本地方法栈则是为了native方法提供服务.

java堆(重点)

  1. 是java虚拟机所管理的最大的一块内存区域.被所有线程共享.唯一目的是存放对象的实例.按照java虚拟机规范:堆中存放着所有的对象实例和数组信息.后续技术更加成熟也有稍微变化的地方.

  2. java堆是垃圾收集器管理的主要区域.因此称为GC堆.按照内存回收方面看,可以分为新生代和老年代.比例默认是1/3新生代,2/3老年代.

  3. java堆可以存放在不连续的内存空间,只要是逻辑上连续即可.当无法分配更多的实例时,会抛出OutOfMemoryError.

  4. 实例对象的组成,可参考:java对象

元空间

  1. 主要是存放被虚拟机加载的类的信息,常量,静态变量,编译后的代码.class对象.

  2. jdk1,8之后出现的,主要在本机内存中,更方便了根据情况扩容.同时,将String类迁移到了堆中.

运行时的常量池

  1. 运行期的常量池是堆的一部分,用于存放编译期生成的字面量和符号引用.

  2. 需要注意的是和局部变量表的局别.同样是存储编译期的数据,不过一个是方法的参数,方法内定义的变量.另一个是存储字面量的常量.

直接内存

直接内存不是jvm的内存.是介于本地方法栈中的native方法,与java堆之间的一种机器中的内存.例如在native方法进行io流的时候进行缓存数据的区域.是实现NIO的主要地方.

对象的访问

在了解了虚拟机运行时候的数据区域后,下面来看下是如何进行对象访问的.

例如: Object obj = new Object();

假设这块方法出现在方法体中,其中Object obj会映射到虚拟机栈的栈桢的局部变量表中的reference类型. new Object()则会映射到java堆中.另外java堆中还必须能够查到次对象类型的数据(对象的类型,父类,实现的接口,方法),这些数据存放在方法区.其中reference方法是如何定位到java堆中的?主要有2种方式:

句柄池访问

在java堆中划分出一块内存作为句柄池,reference存储的是句柄池的地址.句柄池内存储着对象实例的数据和对象类型的数据.

主要优点是:对象进行移动时,只需要改变句柄池中的对象实例信息.

直接指针访问方式

reference存储的是对象实例的数据,这是就要考虑如何放置对象类型的数据的地址.主要优点是节省了一次指针的定位,速度更快.sun 的hotspot所用的虚拟机主要是采用此方案.

OOM异常

java堆溢出

通过-Xms指定最小值,-Xmx指定最大值

提示是java heap space,查看对象信息,确定是否是要用的,如果对象必须要或者,则是内存溢出,此时加大虚拟机内存.如果对象不必须活着,则是内心泄露,此时定位到对象点,进行处理.

虚拟机栈和本地方法栈溢出

通过-Xss128k,表示指定大小为128k,主要有2种异常:

  1. 线程请求的深度大于虚拟机允许的最大深度:Stack OverflowError.

  2. 虚拟机在扩展栈时,无法请求到足够的内存:OOM

元空间

在jdk1.8之后,hotspot对jvm架构进行了改变,将类的元数据放在了本地内存里面,静态变量和常量池放在了java堆中,移除了永久代,元数据是描述数据之间的关系的数据,其表现形式是1.5之后的注解,1.5之前的xml文件.由于在本地内存中,可以避免两个项目引用了同样的jar包,会出现大量的相同类信息引发的oom异常.避免了与老年代的资源竞争.

可用-XX:MetaspaceSize 和 -XX:MaxMetaspaceSize指定元数据大小

本机直接内存溢出

通过-XX:MaxDirectMemorySize指定大小,仅是抛出OOM溢出.

Last updated

Was this helpful?