深入理解Java运行时数据区_动力节点Java学院整理

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

要理解JVM的运行时数据区, 必须先要理解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方法。

(0)

相关推荐

  • 详解Java虚拟机管理的内存运行时数据区域

    详解Java虚拟机管理的内存运行时数据区域 概述 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁. 程序计数器 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基

  • 深入理解Java运行时数据区_动力节点Java学院整理

    JVM体系结构和运行时数据区概述 要理解JVM的运行时数据区, 必须先要理解JVM的体系结构, 因为虚拟机的体系结构基本上解释了"为什么会有这些运行时数据区" . JVM的体系结构如下: 由此可见, 运行时数据区的划分, 是和JVM的体系结构相关的. 本文主要介绍运行时数据区的划分, 对体系结构不做深入的讲解. 简单概括一下, 类加载器子系统用于将class文件加载到虚拟机的运行时数据区中(准确的说应该是方法区) . 可以认为执行引擎是字节码的执行机制, 一个线程可以看做是一个执行引擎

  • Java运行时数据区概述详解

    Java 虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途,如图所示: 程序计数器 程序计数器是一块比较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器. 在虚拟机的概念模型中(仅是概念模型,各种虚拟机可能会通过一些更加高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 如果线程正在执行一个Java方法,则这个计数

  • 深入理解Java中的final关键字_动力节点Java学院整理

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使用final关键字的实例.final经常和static一起使用来声明常量,你也会看到final是如何改善应用性能的. final关键字的含义? final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如

  • Java 中的 BufferedReader 介绍_动力节点Java学院整理

    BufferedReader 介绍 BufferedReader 是缓冲字符输入流.它继承于Reader. BufferedReader 的作用是为其他字符输入流添加一些缓冲功能. BufferedReader 函数列表 BufferedReader(Reader in) BufferedReader(Reader in, int size) void close() void mark(int markLimit) boolean markSupported() int read() int

  • Java异常继承结构解析_动力节点Java学院整理

    Java异常类层次结构图: 异常的英文单词是exception,字面翻译就是"意外.例外"的意思,也就是非正常情况.事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误.比如使用空的引用.数组下标越界.内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图.错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的.假若程序在运行期间出现了错误

  • Java设计模式之备忘录模式_动力节点Java学院

    定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和

  • Java Date类常用示例_动力节点Java学院整理

    Date类 在JDK1.0中,Date类是唯一的一个代表时间的类,但是由于Date类不便于实现国际化,所以从JDK1.1版本开始,推荐使用Calendar类进行时间和日期处理.这里简单介绍一下Date类的使用. 1.使用Date类代表当前系统时间 Date d = new Date(); System.out.println(d); 使用Date类的默认构造方法创建出的对象就代表当前时间,由于Date类覆盖了toString方法,所以可以直接输出Date类型的对象,显示的结果如下: Sun Ma

  • Java Runtime类详解_动力节点Java学院整理

    一.概述 Runtime类封装了运行时的环境.每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接.一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用.一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为. 当不被信任的代码调用任何Runtime方法时,常常会引起SecurityExc

  • Java Collections集合继承结构图_动力节点Java学院整理

    面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式. 数组虽然也可以存储对象,但长度是固定的:集合长度是可变的,数组中可以存储基本数据类型,集合只能存储对象. 集合类的特点:集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象. 集合框架图 Collection (集合的最大接口)继承关系 --List 可以存放重复的内容 --Set 不能存放重复的内容,所以的重复内容靠hashCode()和equals()两个

  • Java中的super关键字_动力节点Java学院整理

    一.super关键字 在JAVA类中使用super来引用父类的成分,用this来引用当前对象,如果一个类从另外一个类继承,我们new这个子类的实例对象的时候,这个子类对象里面会有一个父类对象.怎么去引用里面的父类对象呢?使用super来引用,this指的是当前对象的引用,super是当前对象里面的父对象的引用. super关键字测试 package com.bjpowernode.test; /** * 父类 * * */ class FatherClass { public int value

随机推荐