深入理解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的静态区。 一个类型的生命周期一般就是整个程序的生命周期。 这也是为什么要慎用静态变量的原因所在, 因为静态变量随类型信息存放在方法区中, 生命周期很长, 如果使用不当, 很容易造成内存泄露。 一个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运行时数据区,布布扣,bubuko.com

时间: 2024-12-18 18:16:49

深入理解Java运行时数据区的相关文章

Jvm基础-Java运行时数据区

最近在看<深入理解Java虚拟机>,里面讲到了Java运行时数据区,这是Jvm基本知识,把读书笔记记录在此.这些知识属于常识,都能查到的,如果我有理解不对的地方,还请指出. 首先把图贴上来,图来自JVM Runtime Data Areas(运行时数据区),感谢. 由上图可知,Java运行时数据区域包括程序计数器.Java虚拟机栈.本地方法栈.Java堆.方法区. 1. 程序计数器 程序计数器用来记录下一条字节码指令,因为CPU是要轮转的,在切换回来之后,Java能够找到下一条要执行的指令.如

深入理解Java执行时数据区

前情回想 在本专栏的前12篇博客中. 我们主要大致介绍了什么是JVM, 而且具体介绍了class文件的格式. 对于深入理解Java, 或者深入理解运行于JVM上的其它语言, 深入理解class文件格式都是必须的. 假设读者对class文件的格式不是非常熟悉, 在阅读本博客以下的文章之前. 建议先读一下前面的12篇博客. 或者參考其它资料, 熟悉class文件的格式.? 在深入理解Java虚拟机究竟是什么?这篇博客中, 我们有提到过, JVM就是一个特殊的进程, 我们运行的java程序. 都运行在

Java运行时数据区

Java虚拟机定义了一些程序运行期间会使用到的数据区域,其中一些会随着JVM的启动而创建,随着JVM的退出而销毁:另外一些则与线程的运行一一对立的,这些数据区域会随着线程的开始而创建,随着线程的结束而销毁.下面是一张Java运行时的数据区模型图: 总的来说,Java运行时数据区域可以分为两个部分:线程共享的区域和线程独享的区域.下面一一对之进行总结. 一.线程共享区域:线程共享区域是指各个线程都会使用到的一块空间区域,它们会在这里申请空间.使用空间.根据具体提供功能不同,可以划分为两个部分,分别

【JVM学习】2.Java虚拟机运行时数据区

来源: 公众号: 猿人谷 这里我们先说句题外话,相信大家在面试中经常被问到介绍Java内存模型,我在面试别人时也会经常问这个问题.但是,往往都会令我比较尴尬,我还话音未落,面试者就会"背诵"一段(Java虚拟机是由堆.方法区.虚拟机栈,吧啦吧啦...),估计心里还一脸自豪的想幸好哥提前在网上搜过,早有准备.每每这个时候,我都不忍心打断,因为"背诵"的真的太顺畅了! 这也怪不得面试者,首先Java虚拟机方面的知识,对中高级程序猿来说,工作中正面接触Java虚拟机的东西

JVM内存结构(运行时数据区)

前言 Java程序的运行是通过Java虚拟机来实现的.通过类加载器将class字节码文件加载进JVM,然后根据预定的规则执行.Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些内存区域被统一叫做运行时数据区.Java运行时数据区大致可以划分为5个部分.在这里要特别指出,我们现在说的JVM内存划分是概念模型.如下图所示: JVM运行时数据区分为5种: 程序计数器 虚拟机栈(java栈) 堆 方法区 本地方法栈 程序计数器 程序计数器是一块较小的内存空间,它可

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

了解Java GC机制,必须先清楚在JVM中内存区域的划分. JVM的体系结构如下:JVM的类加载系统.执行引擎系统.垃圾回收器都是去访问  运行时数据区. JVM管理的内存区域分为几个模块: (未完待续...)

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

运行时数据区示意图 1. 程序计数器 占用一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器.主要用来记录线程执行到哪条语句了,分支.循环.跳转.异常处理.线程恢复等功能都需要依赖这个计数器来完成. 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址:如果正在执行的时Native方法,这个计数器值则为空.此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域. 2. Java 虚拟机栈 线程私有,生命

Java 虚拟机运行时数据区介绍

引言 Java 虚拟机(Virtual Machine)在执行 Java 程序时, 会将它所管理的内存划分为不同的数据区,这些分块有不同的功能,理解它们各自的特点,对于深入理解java程序运行机制和调优Java代码,具有重要意义.其实个人觉得,理解 Java 虚拟机的原理,也没想象中的复杂(也许我的想法有点天真啊^_^),它的运行机制,就像一个小型操作系统,这也许就是虚拟机的来源吧. Java VM运行时数据区结构图 运行时数据区域解释 本地方法栈 该区域所发挥的作用与虚拟机栈类似,只不过它是为

Java虚拟机运行时数据区结构

本文部分参考自<Java虚拟机规范(Java SE 7版)>的中译本和周志明的<深入理解Java虚拟机>,另加个人理解.原书对Java虚拟机运行时数据区描述只有6页,同时参考其他网络网资料,个人能力所限,不排除存在认知错误. JVM将程序运行期间使用的内存划分为若干个运行时数据区,其中一些会随着虚拟机启动而创建,随着虚拟机退出而销毁.另外一些与线程一一对应,随着线程开始而创建,随着线程结束而销毁.数据区划分如下图所示意: Java堆(Java Heap) 在JVM中,Java堆是可