阅读 93

JVM内存模型

线程私有得: 程序计数器 虚拟机栈 本地方法栈

线程共享得: 堆 方法区 直接内存(非运行时数据区得一部分)

1. 程序计数器

程序计数器是一块较小得内存区域,可以看作是当前线程所执行得字节码得行号指示器。字节码解释器工作时通过改变这个计数器得值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等功能都需要依赖这个计数器来完成。

另外,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序技术器,各线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

作用:

(1) 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行,选择,循环,异常处理。

(2) 在多线程的情况下,程序计数器用于记录当前线程的执行的位置,从而当线程被切换回来的时候能够知道线程上次运行到哪儿了。

注意:程序计数器是唯一一个不会出现OOM的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

2. java虚拟机栈

线程私有,描述的是java方法执行的内存模型,每次方法调用的数据都是通过栈传递的。实际上虚拟机栈有一个个栈帧组成,每一个栈帧都拥有:局部变量表操作数栈 动态链接 方法出口等信息。

局部变量表主要存放了编译期可知的各种数据类型(boolean byte char short int float long double ),对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个对象的句柄或其他与此对象相关的位置)

虚拟机栈会出现两种错误:StackOverFlowError 和OOM

StackOverFlowError: 若虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度远超过当前Java虚拟机栈的最大深度的时候,就抛出StackOverFlowError错误

OutOfMemoryError: Java虚拟机栈的内存大小可以动态扩展,如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OOM

扩展:那么方法/函数如何调用?

Java栈可以类比数据结构中的栈,Java栈中保存的主要内容是栈帧,每一次函数调用都会有一个对应的栈帧被压入Java栈,每一个函数调用结束后,都会有个栈帧被弹出。

Java方法两种返回方式 return语句 抛出异常 不管那种都会导致栈帧被弹出。

3. 本地方法栈

和虚拟机栈发挥作用相似,区别是:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

4. 堆

线程共享 存放对象的实例,几乎所有的对象实例以及数组都在这里分内内存。由于现在收集器基本都采用分代收集算法,Java堆可以细分为:新生代和老年代,再细致一点:Eden空间,From Survivor,To Survivor空间,进一步划分是为了更好得回收内存,或者更快得分配内存。

对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 

-XX:MaxTenuringThreshold 来设置。

堆最容易出现OOM错误

(1) OOM: gc overHead Limit Exceeded :当JVM花太多时间执行垃圾回收并且只能回收很少得堆空间时,就会发生此错误

(2) OOM:Java head space 假如在创建新的对象时,堆内存中得内存空间不足以存放新创建得对象 就会发生此错误。 -Xmx设置

5.方法区

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

文章分类
后端
文章标签