深入理解Java执行时数据区

Posted ldxsuanfa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Java执行时数据区相关的知识,希望对你有一定的参考价值。

前情回想


在本专栏的前12篇博客中。 我们主要大致介绍了什么是JVM, 而且具体介绍了class文件的格式。 对于深入理解Java, 或者深入理解运行于JVM上的其它语言, 深入理解class文件格式都是必须的。 假设读者对class文件的格式不是非常熟悉, 在阅读本博客以下的文章之前。 建议先读一下前面的12篇博客。 或者參考其它资料, 熟悉class文件的格式。?


深入理解Java虚拟机究竟是什么?这篇博客中, 我们有提到过, JVM就是一个特殊的进程, 我们运行的java程序。 都运行在一个JVM进程中, 这个进程的作用就是载入class文件, 而且运行class文件里的代码。 当然。 从一个class文件的载入。 到准备好可运行之前。 另一段非常长的路要走, 以后的文章会具体介绍这个过程。 既然虚拟机作为一个虚拟的计算机, 来运行我们的程序, 那么在运行的过程中。 必定要有地方存放我们的代码(class文件)。 在运行的过程中, 总会创建非常多对象, 必须有地方存放这些对象; 在运行的过程中, 还须要保存一些运行的状态。 比方, 将要运行哪个方法, 当前方法运行完毕之后, 要返回到哪个方法等信息。 所以, 必须有一个地方来保持运行的状态。

上面的描写叙述中, “地方”指的当然就是内存区域, 程序运行起来之后。 就是一个动态的过程, 必须合理的划分内存区域。 来存放各种数据。 所以。 在本文中, 将会具体介绍JVM的运行时数据区。

?


JVM体系结构和运行时数据区概述


要理解JVM的运行时数据区。 必须先要理解JVM的体系结构, 由于虚拟机的体系结构基本上解释了“为什么会有这些运行时数据区” 。 在深入理解Java虚拟机究竟是什么?这篇文章中也简单的提到过JVM的体系机构, 这里再具体的解说一下。 JVM的体系结构例如以下:

技术图片

由此可见, 运行时数据区的划分。 是和JVM的体系结构相关的。 本文主要介绍运行时数据区的划分, 对体系结构不做深入的解说。 简单概括一下, 类载入器子系统用于将class文件载入到虚拟机的运行时数据区中(准确的说应该是方法区) 。 能够觉得运行引擎是字节码的运行机制, 一个线程能够看做是一个运行引擎的实例。 以下介绍运行时数据区:


JVM运行时数据区


方法区

在字面意思上。 “方法区”这个词会让人产生误解。由于方法区存放的不仅仅是方法, 它存放的是类型信息。我们在敲代码的时候, 差点儿总是在和类。 对象打交道, 我们知道依据一个类能够创建对象。 一般来说。 我们操纵的是对象。 訪问对象的属性。 调用对象的方法等, 可是我们要思考这样一个问题, 虚拟机依据什么信息知道怎样创建对象的呢? 当然是依据这个对象的类型信息, 可是这个类型信息在哪里呢?如今我们知道是在方法区中。 那么类型信息是被谁载入到方法区中的呢?由上面的体系结构图, 我们能够知道是类载入器子系统?那么所谓的类型信息, 都包括什么信息呢?这些信息又是怎样存放的呢?这里的类型信息, 能够笼统的觉得就是我们前面解说过的一个class文件,类载入器子系统将会提取class文件里面的类型信息,并将这些类型信息存放到方法区中。

?至于方法区中怎样存放一个类型数据, 是和JVM的具体实现相关的。 可是无论怎样实现, 一个类的类型信息总是会包括例如以下信息:


类的全限定名
当前类的直接父类的全限定名
这个类是接口类型, 类类型。 还是枚举类型
类的訪问修饰符信息
当前类型的超接口的全限定名
当前类型的常量池
字段信息
方法信息


假设对class文件格式比較熟悉的话。 能够看出, 这些信息都是在class文件里描写叙述过的。 由于我们无法看到类型信息具体是怎样存储的。 可是大致能够将类型信息看做一个class文件, 这有助于我们的理解。

以下再次列出class文件结构的表格。读者能够对照class文件里的内容到类型数据上。 该表中的各种数据已经在前面的博客中具体解说过:

技术图片

?类型数据中。除了这些基本信息外, 类型信息还包括以下两个方面:


一个到类的ClassLoader对象的引用
一个到表示该类的Class实例对象的引用

静态变量存储区


由于之前的博客中具体介绍过class文件的格式, 对上面的一些基本信息我们可能比較熟悉。 可是对这两种信息就比較陌生了。

事实上说来也简单,每一个class都是被一个类载入器载入到方法区的, 类型信息中的到类的ClassLoader对象的引用, 表明了当前的类是被哪个类载入器载入的, 这个信息同一时候也标示了当前的类型的名称空间。?


每当一个class文件被成功的载入到方法区中, JVM总会创建一个Class对象, 来唯一标示这个类。

这个Class对象能够看做是类载入过程的产物。 由于它描写叙述了整个类型信息。 而Java中的反射也是针对的类型信息。 所以这个Class对象是反射的基石。 大多数反射API都是依据Class对象来实现的。??


而静态变量也是存在于类型信息中, 能够这么说, 类型信息中, 会有专门的区域存放类的静态变量。 与存在于对象中的实例变量不同, 静态变量存在于类型数据中, 每一个类型仅仅有一份。所以也叫类变量。?


方法区是一个相对来说比較固定的内存区, 由于它存放的是类型信息, 而类型信息在被载入到方法区中之后, 除了必要的连接和初始化。 一般不会有较大修改。普通情况下, JVM也不会卸载类型信息。 所以方法区也能够称为JVM的静态区。 一个类型的生命周期一般就是整个程序的生命周期。 这也是为什么要慎用静态变量的原因所在, 由于静态变量随类型信息存放在方法区中。 生命周期非常长, 假设使用不当。 非常easy造成内存泄露。

一个JVM实例中仅仅存在一个方法区, 方法区中的全部类型数据被全部线程共享。


方法区是存放类型数据的, 而堆则是存放运行时产生的对象的。 和C++不同的是。 Java仅仅能在堆中存放对象。 而不能在栈上分配对象, 全部运行时产生的对象全部都存放于堆中, 包括数组。

我们知道, 在Java中, 数组也是对象。一个JVM实例中仅仅有一个堆, 全部线程共享堆中的数据(对象) 。?


Java虚拟机支持几种不同的创建对象的指令, 如new 。 anewarray等。 这些指令运行的结果就是在堆中分配内存。 并创建对象。 可是Java虚拟机的指令集中并不包括不论什么释放内存的指令, 因而我们也就不能手动释放内存。 全部被创建的对象都会被一个叫做垃圾收集器(GC)的模块自己主动回收。 垃圾收集器有不同的实现方式, 他们以 特定的方式推断对象是否过期, 并以特定的方式对对象进行回收, 关于垃圾收集的话题不是本文的重点, 这里就不多说了。 我们仅仅要知道:全部创建的对象都存在堆中。 而垃圾收集器会自己主动回收过期的对象。 所以,JVM的堆区是垃圾收集器的“重点管理区” 。



Java栈

Java栈是一个线程的运行区域, 它保存着一个线程中的方法的调用状态, 也能够说, 一个Java线程的运行状态。 都由一个Java栈来保存。 在这个栈中, 每一方法相应一个栈帧, 请注意区分栈帧和栈这两个概念。

栈指的是整个线程的运行栈, 栈帧是栈中的一个单位, 每一个方法相应一个栈帧。 JVM会对Java栈运行两种操作: 压栈和出栈。 这两种操作在运行时都是以帧(栈帧)为单位的。 当调用了一个新的方法, 就会压入一个栈帧, 当一个方法调用完毕, 就会弹出这种方法的栈帧, 回到调用者的栈帧。?


举例来说。 假设方法a调用了方法b, 而方法b中调用了方法c。 这个过程中的方法调用和返回的装状态是这种(当中图中两条虚线之间表示Java栈,每一个方块表示一个特定方法的栈帧)
技术图片


Java栈上的全部数据都是线程私有的, 也就是说, 每一个线程都会有自己的Java栈, 不会相互訪问其它Java栈中的数据。?


PC寄存器

pc寄存器用于存放一条指令的地址, 这条指令就是虚拟机要运行的下一条指令。pc寄存器和线程相关联, 每一个线程都有一个PC寄存器。



本地方法栈

我们知道Java能够和C/C++互调。假设当前线程运行的代码是C/C++写的本地代码。 那么这些方法就在本地方法栈中运行。而不会在Java栈中运行, Java栈中仅仅运行Java方法。




以上是关于深入理解Java执行时数据区的主要内容,如果未能解决你的问题,请参考以下文章

深入理解java虚拟机(转)

深入理解Java虚拟机 学习总结

深入理解java:1.3.1 JVM内存区域的划分(运行时数据区)

《JVM系列》 第三章 -- 深入理解JVM运行时数据区

《深入理解Java虚拟机》笔记01 -- 运行时数据区

深入理解JVM:JVM执行时数据区域分类