jvm之内存结构详解

       JVM的内存结构和各个内存区域的作用,对于理解Java内存机制、工作原理有着较大帮助。首先看一下《深入理解Java虚拟机(第二版)》给出的JVM内存结构图:

                                        

1.JVM结构分析

程序计数器:

       当执行一条指令时,首先需要根据程序计数器(PC)中存放的指令地址,将指令由内存取到指令寄存器中,此过程称为“取指令”。与此同时,PC中的地址或自动加1或由转移指针给出下一条指令的地址。此后经过分析指令,执行指令。完成第一条指令的执行,而后根据PC取出第二条指令的地址,如此循环,执行每一条指令。

       程序计数器是一块较小的内存空间,它的作用是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。每个线程都拥有一个属于本线程的PC寄存器,当线程执行某个java方法时,PC寄存器的内容总是下一条被执行指令的”地址”,这里的”地址”可以是一个本地指针,也可以是在方法字节码中相对于该方法起始指令的偏移量。如果该线程正在执行一个本地方法,那么PC寄存器的值是”Undefined”。 

方法区:

       方法区(Method Area)是用于存储类结构信息的地方,包括常量池、静态变量、构造函数等类型信息,类型信息是由类加载器在类加载时从类文件中提取出来的。方法区中的常量池,包含着一些常量和符号引用(加载类的连接阶段中的解析过程会将符号引用转换为直接引用),方法区是线程共享的。

堆:

       堆(heap)是存储java实例或者对象的地方,是GC的主要区域,同样是线程共享的内存区域。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不在实用的数据。堆的缺点是由于运行时要动态分配内存,存取速度慢。

虚拟机栈:

       虚拟机栈(Java Virtual Machine Stacks)和线程是紧密联系的,每创建一个线程时就会对应创建一个Java栈。其生命周期和线程相同,其由一系列帧组成,每个帧保存一个方法的局部变量、操作数栈、动态链接和返回地址。当我们每调用一个方法,都会创建一个栈帧,并压栈。每一个方法从调用到最终返回结果的过程,就对应一个栈帧从入栈到出栈的过程。栈的优势是存取速度比堆要快。栈的缺点是存在栈的数据大小和生存期必须是确定的,缺乏灵活性。

扫描二维码关注公众号,回复: 2804750 查看本文章

本地方法栈:

      本地方法栈和虚拟机栈的作用相似,不过虚拟机栈是为用户服务的,而本地方法栈是为Java底层(即jvm本身,jvm调用的native方法)方法服务的。

       这里对虚拟机栈和本地方法栈的区别做进一步说明:本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。我们在进行理解时,同意记为栈即可。

2.结合代码作进一步说明

首先看代码:

public class MemoryStructureTest { // 运行时jvm会把该类的代码放入方法区
	
	public static String str = "abc";// 此静态变量存于方法区的常量池
	
	public static void main(String[] args) {
		String string = new String("abc");// “string”即变量名放入栈,"abc"即值放入堆
		System.out.println(string);
	}
}

具体内存分配请看代码注释。关于jvm为什么要这样分配,读者在阅读第一部分的关于堆栈的优势和劣势后,结果可想而知了。

特殊说明:

      基本类型,即定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在 。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就直接存在于栈中。  

猜你喜欢

转载自blog.csdn.net/m0_38075425/article/details/81632258