专注Java教育14年 全国咨询/投诉热线:444-1124-454
赢咖4LOGO图
始于2009,口口相传的Java黄埔军校
首页 hot资讯 JVM模型的介绍

JVM模型的介绍

更新时间:2022-02-14 10:39:18 来源:赢咖4 浏览1132次

java内存分配

JVM定义了若干个程序执行期间使用的数据区域。这个区域里的一些数据在JVM启动的时候创建,在JVM退出的时候销毁。而其他的数据依赖于每一个线程,在线程创建时创建,在线程退出时销毁。

java按照内存使用区域可以分为四个部分:堆、栈、方法区和程序计数器;其中栈又可以分为两大部分:java虚拟机栈和本地方法栈;下面我将通过自己的学习与别人总结的参考见解来介绍下jvm

程序计数器

程序计数器是一块较小的内存空间,它被用来记录当前指令执行程序的位置,在内部通过改变计数器的位置来确定下一条指令执行程序的位置;

我们知道java多线程的实质是通过线程轮流切换并分配处理执行事件来实现的,而在任何一个确定的时刻,一个处理器只会执行一个线程中的一条指令。为了能让线程切换后仍然能恢复到原来的位置,每个线程都需要有一个独立的程序计数器,他们之间互不影响,所以,程序计数器是线程私有的内存区域

如果一个线程正在执行一个java方法,那么程序计数器记录的就是正在执行的虚拟机字节码指令的地址;如果正在执行一个native方法,那么程序计数器的值就为空;

程序计数器是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域,在四个区域中只有它不会出现内存溢出的情况

栈之java虚拟机栈

java虚拟机栈描述的是java方法的内存模型;每个方法在被执行的同时都会创建一个java虚拟机栈(栈帧),用于存储局部变量表、操作栈、动态链接和方法出口等信息,因此java虚拟机栈也是线程私有的;

类似于动画是由一栈一栈的栈帧切换产生的,程序的执行也是由栈帧的切换产生的,只是这些栈帧中存放的是方法的局部变量、操作栈、动态链接和方法出口等信息;每一个方法被调用直至执行完成的过程,就是一个栈帧在虚拟机栈中从入栈到出栈的过程;

对于执行引擎来说,只有栈顶的栈帧是有效的,称为当前栈帧,这个栈帧关联的方法称为当前方法,执行引擎所有的字节码指令都值针对当前栈帧进行操作;

通常我们所说的变量存储在栈中是不严谨的,在java虚拟机栈中存放的是对应方法的局部变量,而且这些局部变量存放在java虚拟机中的局部变量表中;对于保存的局部变量分类:java中的基本数据类型变量的值存放在局部变量表,而对于引用类型变量来说,局部变量表只会存放对象的引用,真正的值存放在堆中;

java虚拟机是使用局部变量表来完成参数值到参数变量表的传递过程的,系统不会为局部变量赋予初始值(对于实例变量和类变量都会赋予初始值)

在java虚拟机规范中,对java虚拟机栈规定了两种异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常;如果虚拟机栈动态扩展时无法申请到足够的内存时,会抛出OutOfMemoryError 异常

栈之本地方法栈

本地方法栈的作用和java虚拟机栈的作用是相类似的;区别在于java虚拟机栈是为java方法服务的,而本地方法栈是为native方法服务的;java虚拟机规范中对本地方法栈中的方法使用的语言、使用方式和数据结构没有强制规范

堆是jvm内存模型中内存空间最大的一块,java堆是被所有线程共享的一块内存区域,在虚拟机启动时随之创建,堆创建的唯一目的就是用来保存对象实例,几乎所有new创建的对象实例都在这里分配内存(随着JIT 编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了)

堆的大小可以通过-Xms和-Xmn两个参数来设置,-Xms为JVM启动时申请的最小内存,默认为操作系统物理内存的1/64但小于1G,-Xmx为JVM可申请的最大内存,默认为物理内存的1/4但小于1G,默认当空余堆内存小于40%时,JVM会增大Heap到-Xmx指定的大小,可通过-XX:MinHeapFreeRation=来指定这个比列;当空余堆内存大于70%时,JVM会减小heap的大小到-Xms指定的大小,可通过XX:MaxHeapFreeRation=来指定这个比列,对于运行系统,为避免在运行时频繁调整Heap的大小,通常-Xms与-Xmx的值设成一样。

堆也是垃圾收集管理的主要区域,通常所说的垃圾回收主要是回收堆中的垃圾对象,因此堆也被称为GC堆

从垃圾回收的角度上来看,由于现在垃圾收集大多是采用分代收集算法,所以java堆还可以细分为:新生代和老年代;

新生代:程序新创建的对象都是从新生代分配内存,新生代由Eden Space和两块相同大小的Survivor Space(通常又称S0和S1或From和To)构成,可通过-Xmn参数来指定新生代的大小,也可以通过-XX:SurvivorRation来调整Eden Space及SurvivorSpace的大小。

老年代:用于存放经过多次新生代GC仍然存活的对象(默认是15次),例如缓存对象,新建的对象也有可能直接进入老年代,主要有两种情况:

1.大对象,可通过启动参数设置-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不在新生代分配,而是直接在老年代分配。

2.大的数组对象,且数组中无引用外部对象。老年代所占的内存大小为-Xmx对应的值减去-Xmn对应的值。

java堆也会出现内存溢出的情况:如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常

方法区

方法区用于存放已被虚拟机加载的类信息、常量、类变量等数据,它也是所有线程共享的一块内存区域,通常叫它为no-heap(用于与堆区分)或者永生代

永生代也会被GC回收,主要针对常量池回收和类型卸载(反射生成大量的临时class等信息)

常量池用于保存编译器生成的各种字节码和符号引用,具有一定的动态性,里面可以保存编译器生成的常量,运行期间的常量也是可以添加到常量池中的,比如String的intern()方法;

方法区也是会出现内存溢出的情况的:当方法区满时,无法再分配内存空间,就会抛出内存溢出的异常

java8中已经没有方法区了,取而代之的是元空间metaspace

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>