JVM 运行时数据区概述

75 阅读4分钟

运行时数据区概述及线程

内存是非常重要的系统资源,是硬盘和 CPU 的中间仓库及桥梁,承载着操作系统和应用程序的实时运行。JVM 内存布局规定了 Java 在运行过程中内存申请、分配、管理的策略,保证了 JVM 的高效稳定运行。不同的 JVM 对于内存的划分方式和管理机制存在着部分差异。结合 JVM 虚拟机规范,来探讨一下经典的 JVM 内存布局

RuntimeDataArea.jpg

可优化部分

针对线程独立享有的资源里面没有太多可以优化的点

而主要是优化线程共用的部分:堆区、堆外内存 方法区(永久代或元空间、代码缓存)。不过有的书籍认为 JIT 产物是(代码缓存)和元空间归为一类合并的(周志明),也有的说法是代码缓存与元空间是分离的(阿里)

元空间01.png

95%的垃圾回收出现在堆区,5%在方法区。但 Java8 之后,元空间(方法区)用的就是本地内存,本地内存一般很大,所以不会轻易的进行垃圾回收。但也有内存溢出的问题,如果 OOM 了就要考虑垃圾回收的问题了

关于线程共享的说明

JVM 定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程开始和结束而创建和销毁

在 Runtime Data Area 中,多线程共享方法区和堆其它部分每个线程独有一份

线程共享.jpg

Runtime Class

Runtime 是 Java 中的一个类,也可以称之为运行时对象,一个 JVM 进程就对应一个 Runtime 实例,一个虚拟机只有一份

runtimeClass.png

Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime() method.

An application cannot create its own instance of this class.

百度翻译一下:

每个Java应用程序都有一个 Runtime 类实例,它允许应用程序与运行应用程序的环境进行交互。当前运行时可以从 getRuntime() 方法获得。

应用程序无法创建自己的此类实例。

注意:每个 JVM 只有一个Runtime 实例,相当于内存结构中的 Runtime Data Area:运行时数据区

线程

  • 线程是一个程序里的运行单元。JVM 允许一个应用有多个线程并行的执行

  • 在 Hotspot JVM 里,每个线程都与操作系统的本地线程直接映射

    当一个 Java 线程准备好执行以后(每个线程里面的 PC、VMS、NMS 准备好以后)

    此时一个操作系统的本地线程也同时创建。Java线程执行终止后,本地线程也会回收(正常执行完,或者出现异常抛出等异常处理机制,都算执行完毕)

    若此线程是最后一个非守护线程,那么 JVM 也会跟着终止

  • 操作系统负责所有线程的安排调度到任何一个可用的 CPU 上。一旦本地线程初始化成功,它就会调用Java线程中的 run() 方法

JVM 系统线程

如果你使用 jconsole (内存监控工具)或者是任何一个调试工具,都能看到在后台有许多线程在运行。这些后台线程不包括调用 public static void main (string[]) 的 main 线程以及所有 main 线程自己创建的线程

这些主要的后台系统线程在 Hotspot JVM 里主要是以下几个:(了解一下)

  • 虚拟机线程∶这种线程的操作是需要 JVM 达到安全点才会出现。这些操作必须在不同的线程中发生原因是他们都需要 JVM 达到安全点,这样堆才不会变化。这种线程的执行类型包括 "stop-the-world" (所以应用都终止)的垃圾收集,线程栈收集,线程挂起以及偏向锁撤销。
  • 周期任务线程∶这种线程是时间周期事件的体现(比如中断),他们一般用于周期性操作的调度执行。
  • GC线程:这种线程对在JVM里不同种类的垃圾收集行为提供了支持。
  • 编译线程:这种线程在运行时会将字节码编译成到本地代码。
  • 信号调度线程:这种线程接收信号并发送给JVM,在它内部通过调用适当的方法进行处理。