郑州市网站空间服务公司,具体的网站建设,wordpress登记打印,大数据营销名词解释专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录
专栏导航
前言
一、运行时数据区
二、方法区
1.方法区介绍
2.方法区在Java虚拟机的实现
3.类的元信息
4.运行时常量池
5.字符串常量池
6.静态变量的存储
总结 前言
JVM作为Java程序的运行环境… 专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录
专栏导航
前言
一、运行时数据区
二、方法区
1.方法区介绍
2.方法区在Java虚拟机的实现
3.类的元信息
4.运行时常量池
5.字符串常量池
6.静态变量的存储
总结 前言
JVM作为Java程序的运行环境其负责解释和执行字节码管理内存确保安全支持多线程和提供性能监控工具以及确保程序的跨平台运行。本文主要介绍了方法区、方法区在Java虚拟机的实现、类的元信息、运行时常量池、字符串常量池、静态变量的存储等内容。 一、运行时数据区
Java虚拟机JVM在运行Java程序期间会创建并维护一系列内存区域这些区域总称为运行时数据区。这些区域根据其用途和特性被严格定义并管理。《Java虚拟机规范》详细规定了这些区域的作用和行为以确保所有Java虚拟机实现的一致性和正确性。
线程不共享区域
程序计数器用于存储当前线程执行的字节码指令地址。这个区域是每个线程独有的不共享。Java虚拟机栈每个线程在创建时都会创建一个虚拟机栈每个方法调用都会创建一个栈帧用于存储局部变量、操作数栈、动态链接和方法出口信息。本地方法栈与虚拟机栈相似本地方法栈为native方法提供服务。
线程共享区域
方法区用于存储已被JVM加载的类信息、常量、静态变量以及即时编译器编译后的代码等数据。堆堆是所有线程共享的区域用于动态分配内存。所有的对象实例以及数组都应当在堆上分配。 二、方法区
1.方法区介绍
方法区是Java虚拟机中的一部分用于存储已被虚拟机加载的类信息、常量、静态变量以及即时编译器编译后的代码等数据。这个区域的设计目标是为所有线程提供共享的、动态类型的数据。它的核心功能是支持类的加载和链接以及提供运行时类型信息。
方法区主要由以下三部分构成
类的元信息Class Metadata这部分保存了关于类的所有基本信息。这些信息在类加载时被创建并存储在方法区中。类的元信息包括类的名称、类的访问权限、类的字节码版本、父类的名称、实现的接口列表、字段和方法信息等。这些信息在运行时被JVM使用以支持类的方法解析、反射操作和动态代理等功能。运行时常量池Runtime Constant Pool运行时常量池是方法区中的一个重要部分它主要负责存储字节码文件中的常量池内容。常量池是字节码文件中的一个特殊部分用于存储各种类型的常量如字面量、类符号引用等。在运行时JVM会根据需要动态地向常量池中添加、删除或修改相应的常量。此外对于每个类其常量池都有一个私有的副本但所有线程共享同一个运行时常量池以确保线程安全。字符串常量池String Constant Pool字符串常量池是运行时常量池的一部分用于存储字符串字面量。在Java程序中每个独特的字符串字面量都会在字符串常量池中创建一个相应的字符串对象。如果一个字符串字面量已经存在于常量池中则不会创建新的对象而是返回对已有对象的引用。这种设计可以有效地节省内存并避免创建重复的字符串对象。
2.方法区在Java虚拟机的实现
方法区是Java虚拟机结构中的重要部分它是《Java虚拟机规范》中定义的一个抽象概念。每款具体的Java虚拟机实现可能会根据规范进行不同的优化和调整。以HotSpot虚拟机为例来探讨其实现细节。
在JDK7及更早的版本中方法区被实现为永久代(PermGen)。它位于Java堆内存区域中并通过虚拟机参数-XX:MaxPermSize来控制其最大大小。然而这种实现方式在JDK8中被彻底改变。
从JDK8开始方法区被移至元空间(Metaspace)中。元空间不再属于Java堆的一部分而是直接建立在操作系统的本地内存中。这种设计使得元空间的大小不再受Java堆大小的限制而是取决于本地内存的大小。可以通过虚拟机参数-XX:MaxMetaspaceSize可以设置元空间的最大大小。 在诊断和监控Java应用时了解这些差异尤为重要。使用如Arthas这样的诊断工具可以查看不同版本的Java虚拟机的内存使用情况。在JDK7中需要关注ps_perm_gen属性来查看方法区的使用情况而在JDK8及之后的版本则需要查看metaspace属性。 案例 为了模拟方法区的溢出情况可以使用如ByteBuddy这样的框架动态生成大量的字节码数据并观察方法区是否会出现内存溢出。 引入ByteBuddy依赖 dependencygroupIdnet.bytebuddy/groupIdartifactIdbyte-buddy/artifactIdversion1.12.23/version/dependency 编写测试案例 public class Demo1 extends ClassLoader {public static void main(String[] args) throws IOException {System.in.read();Demo1 demo1 new Demo1();int count 0;while (true) {String name Class count;ClassWriter classWriter new ClassWriter(0);classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name, null, java/lang/Object, null);byte[] bytes classWriter.toByteArray();demo1.defineClass(name, bytes, 0, bytes.length);System.out.println(count);}}
} 在JDK7上执行十几万次操作就可能出现错误而在JDK8上尽管内存使用量会直线上升但程序并不会出现错误。 3.类的元信息
方法区也被称为元空间是Java虚拟机中用于存储类的元数据信息的区域。这些元信息通常被称为InstanceKlass对象包含了关于类的基本信息例如类的名称、父类的名称、实现的接口、成员变量和方法等。这些信息在类的加载阶段被完全建立并存储在方法区中。
InstanceKlass对象是类元信息的核心它不仅包含了类的静态信息如字段和方法还包含了类的动态行为信息如字节码信息和常量池等。这些信息在运行时被JVM用于支持诸如反射、动态类加载和异常处理等功能。 需要注意的是方法区的实现和组织方式可能会因JVM的实现而有所不同。例如在一些JVM实现中方法区可能会被组织成一个或多个哈希表以快速查找类的元信息。而在其他实现中可能会使用其他数据结构或算法来组织和管理这些信息。
4.运行时常量池
在Java的内存区域中方法区用于存储类的元数据信息。然而除了存储类的元信息之外方法区还包含了一个重要的部分运行时常量池。
运行时常量池是Java虚拟机在运行时创建的一个数据结构用于存储字节码中的常量池内容。常量池是字节码文件中的一个特殊部分包含了程序中的常量值例如字符串字面量、整数等。这些常量在字节码文件中通过编号索引的方式进行访问这种常量池称为静态常量池。
当字节码文件被加载到内存中时静态常量池的内容会被复制到运行时常量池中。与静态常量池不同运行时常量池中的常量可以直接通过内存地址进行访问因此具有更高的访问速度。这种运行时常量池的设计使得Java程序在运行时能够快速地访问和操作常量提高了程序的执行效率。 5.字符串常量池
在运行时数据区的方法区中除了类的元信息和运行时常量池外还有一个特别重要的区域那就是字符串常量池。字符串常量池主要负责存储字节码文件中定义的常量字符串内容。例如在代码中定义的常量字符串“123”这个字符串就会存放在字符串常量池中。 早期的设计中字符串常量池被视为运行时常量池的一部分它们共享相同的存储空间。然而随着技术的进步和优化需求的变化Java虚拟机对这两者的存储区域进行分离。这种调整的主要原因在于字符串常量池和运行时常量池的功能和职责存在明显的差异。运行时常量池主要负责管理动态编译和类的加载而字符串常量池则专注于存储和管理程序中定义的常量字符串。 6.静态变量的存储
在Java的运行时数据区中静态变量也称为类变量的存储位置在不同版本的Java中有所不同。
在JDK 6及之前的版本中静态变量是存放在方法区中的确切地说是在永久代PermGen中。然而随着Java的发展这个设计逐渐暴露出一些问题例如内存溢出和空间限制等。
从JDK 7开始Java对运行时数据区进行了重新设计其中最显著的变化就是将静态变量从永久代移出并存放在了堆内存中的Class对象中。这种变化不仅解决了永久代存在的问题还使得静态变量的存储更加符合其作为类级别的变量的特性。每个Class对象都包含了该类的静态变量信息这些信息随着类的加载而加载到内存中。这种设计使得静态变量的访问更加直接和高效因为它们不再需要从方法区中查找而是可以直接从Class对象中获取。 总结
JVM是Java程序的运行环境负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了方法区、方法区在Java虚拟机的实现、类的元信息、运行时常量池、字符串常量池、静态变量的存储等内容希望对大家有所帮助。