JVM 组成部分


JVM

  • JVM(Java Virtual Machine,即 Java 虚拟机)是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的;
  • Java 使用 JVM 屏蔽了与具体平台相关的信息,使其在不同平台上运行时不需要重新编译,只需生成在 JVM 上运行的目标代码(字节码),就可以在多种平台上不加修改地运行;

1. JVM 的组成部分

JVM 组成部分 说明
程序计数器(Program Counter Register) 是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器
Java 堆(Java Heap) 是 Java 虚拟机中内存最大的一块,在虚拟机启动时候创建,为所有创建的对象和数组分配内存空间,被 JVM 中所有的线程共享
Java 虚拟机栈(Java Virtual Machine Stacks) 即我们常说的方法栈,它是 Java 方法执行的内存模型,每个方法在执行的同时都会创建一个线帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息
本地方法栈(Native Method Stack) 与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的
方法区(Methed Area) 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据
  • 图解:
    1
  • 运行时常量池(Runtime Constant Pool):Java 语言并不要求常量一定只能在编译期产生,运行期间也可能产生新的常量,这些常量被放在运行时常量池中;
  • 本地库就是系统的一些库,比如 native 方法,是 c/c++ 写的 Java 可以使用的;

2. Java 1.8 之后的改动

  • 永久代(PermGen):方法区是 JVM 的规范,而永久代是方法区的一种实现,并且只有 HotSpot 才有 “PermGen space”,而对于其他类型的虚拟机并没有 “PermGen space”;在 JDK 1.8 中,HotSpot 已经没有 “PermGen space” 这个区间了,取而代之是元空间(Metaspace);

  • 元空间(MetaSpace):在 JDK 1.8 中,永久代已经不存在,存储的类信息、编译后的代码数据等已经移动到了元空间中,元空间并没有处于堆内存上,而是直接占用的本地内存(NativeMemory);

  • 方法区:虽然 HotSpots 取消了永久代,但是方法区是一个规范,它一直存在。取代永久代的就是元空间,元空间与永久代存储位置不同,永久代物理是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存;元空间与永久代存储内容不同,元空间存储类的元信息,而静态变量和常量池等被并入堆中。相当于永久代的数据被分到了堆和元空间中;

  • 图解:
    2

  • 元空间实际工作用来做 JVM 缓存,它的本质和永久代类似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

    • -XX:MetaspaceSize 初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时 GC 会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过 MaxMetaspaceSize 时,适当提高该值;
    • -XX:MaxMetaspaceSize 最大空间,默认是没有限制的;
  • 除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:

    • -XX:MinMetaspaceFreeRatio 在 GC 之后,最小的 Metaspace 剩余空间容量的百分比,减少为分配空间所导致的垃圾收集;
    • -XX:MaxMetaspaceFreeRatio 在 GC 之后,最大的 Metaspace 剩余空间容量的百分比,减少为释放空间所导致的垃圾收集;
  • 直接内存(Direct Memory):并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现。在JDK 1.4 中新加入了 NIO(NewInput/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在Java 堆里面的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据;

发布了255 篇原创文章 · 获赞 258 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Regino/article/details/104976187