当前位置: 首页 > 后端技术 > Java

JAVA基础的JVM内存区

时间:2023-04-01 17:07:25 Java

今天开始复习JVM的知识,看一遍忘记,再看一遍又忘记。...程序计数器(ProgramCounterRegister)是一块很小的内存空间,可以看作是当前线程执行的字节码的行号指示符。Java虚拟机栈(JavaVirtualMachineStack)也是线程私有的,其生命周期与线程相同。虚拟机栈描述了Java方法执行的线程内存模型:每个方法执行时,Java虚拟机都会同步创建一个栈帧(StackFrame)来存放局部变量表、操作数栈、动态连接、方法导出信息.每个方法被调用到执行完成的过程对应于一个栈帧在虚拟机栈中从入栈到出栈的过程。局部变量表存放了Java虚拟机的各种基本数据类型(boolean、byte、char、short、int、float、long、double)和对象引用(referencetype,不等同于对象本身,可能是是指向对象起始地址的引用指针,也可能指向代表对象的句柄或与对象相关的其他位置)和returnAddress类型(指向字节码指令的地址)。原生方法栈(NativeMethodStacks)与虚拟机栈非常相似。不同的是,虚拟机栈是为虚拟机执行Java方法(即字节码)服务的,而native方法栈是为虚拟机服务的。机器使用的本地(Native)方法服务。Java堆是虚拟机启动时创建的所有线程共享的内存区域。这块内存区的唯一用途就是存放对象实例,Java世界中“几乎”所有的对象实例都在这里分配内存。方法区(MethodArea)和Java堆一样,是各个线程共享的一块内存区域。用于存放已经被虚拟机加载过的类型信息、常量、静态变量、实时编译器编译后的代码缓存等数据。也称为非堆。运行时常量池是方法区的一部分。Class文件中除了类版本、字段、方法、接口等描述信息外,还有一个常量池表(ConstantPoolTable),用于存放编译过程中产生的各种字面量和符号引用。这部分内容会在类加载后存储在方法区的运行时常量池中。直接内存(DirectMemory)不属于虚拟机运行时数据区,也不是《Java虚拟机规范》中定义的内存区。但是这部分内存也经常被使用,也有可能导致OutOfMemoryError异常。new的操作,在常量池中查询该类的引用,检查该引用是否已经被加载、解析和初始化,在java堆中分配内存,设置元数据、哈希码、gcage等,并执行构造函数。对象在堆内存中的存储布局可以分为对象头(Header)、实例数据(InstanceData)和对齐填充(Padding)三部分。对象头部分包括两类信息。第一种用于存储对象本身的运行时数据,比如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;对象头的另一部分是类型指针,即对象指向其类型元数据的指针,Java虚拟机通过这个指针来判断该对象是哪个类的实例。实例数据部分是对象实际存储的有效信息,即程序代码中定义的各类字段的内容,无论是继承自父类还是定义在子类中,都必须记录。对象的第三部分是alignmentpadding,不一定存在也没有什么特殊意义,只是起到占位符的作用。如果数据部分不是8的整数倍,则需要填充为8的倍数。后续使用这个对象时,我们的Java程序会通过引用栈上的数据来操作堆上的具体对象。如果使用句柄访问,Java堆中的一块内存可能被划分为一个句柄池。引用存储对象的句柄地址,句柄中包含对象实例数据和类型数据的具体地址信息;使用直接指针进行访问,Java堆中对象的内存布局必须考虑如何放置访问类型数据的相关信息。对象地址直接存储在引用中。如果只访问对象本身,则不需要额外的间接访问开销。