`
gzcj
  • 浏览: 287046 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论
阅读更多

方法区
      在一个jvm实例的内部,类型信息被存储在一个称为方法区的内存逻辑区中。类型信息是由类加载器在类加载时从类文件中提取出来的。类(静态)变量也存储在方法区中。

      jvm实现的设计者决定了类型信息的内部表现形式。如,多字节变量在类文件是以big-endian存储的,但在加载到方法区后,其存放形式由jvm根据不同的平台来具体定义。jvm在运行应用时要大量使用存储在方法区中的类型信息。在类型信息的表示上,设计者除了要尽可能提高应用的运行效率外,还要考虑空间问题。根据不同的需求,jvm的实现者可以在时间和空间上追求一种平衡。

      因为方法区是被所有线程共享的,所以必须考虑数据的线程安全。假如两个线程都在试图找lava的类,在lava类还没有被加载的情况下,只应该有一个线程去加载,而另一个线程等待。方法区的大小不必是固定的,jvm可以根据应用的需要动态调整。同样方法区也不必是连续的。方法区可以在堆(甚至是虚拟机自己的堆)中分配。jvm可以允许用户和程序指定方法区的初始大小,最小和最大尺寸。方法区同样存在垃圾收集,因为通过用户定义的类加载器可以动态扩展java程序,一些类也会成为垃圾。jvm可以回收一个未被引用类所占的空间,以使方法区的空间最小。

 

类型信息
对每个加载的类型,jvm必须在方法区中存储以下类型信息:
一 这个类型的完整有效名
二 这个类型直接父类的完整有效名(除非这个类型是interface或是 java.lang.Object,两种情况下都没有父类)
三 这个类型的修饰符(public,abstract, final的某个子集)
四 这个类型直接接口的一个有序列表

类型名称在java类文件和jvm中都以完整有效名出现。在java源代码中,完整有效名由类的所属包名称加一个".",再加上类名组成。例如,类Object的所属包为java.lang,那它的完整名称为java.lang.Object,但在类文件里,所有的"."都被
斜杠“/”代替,就成为java/lang/Object。完整有效名在方法区中的表示根据不同的实现而不同。

除了以上的基本信息外,jvm还要为每个类型保存以下信息:
 类型的常量池( constant pool)
 域(Field)信息
 方法(Method)信息
 除了常量外的所有静态(static)变量

 

常量池
jvm为每个已加载的类型都维护一个常量池。常量池就是这个类型用到的常量的一个有序集合,包括实际的常量(string,
integer, 和floating point常量)和对类型,域和方法的符号引用。池中的数据项象数组项一样,是通过索引访问的。
因为常量池存储了一个类型所使用到的所有类型,域和方法的符号引用,所以它在java程序的动态链接中起了核心的作用。

 

域信息
jvm必须在方法区中保存类型的所有域的相关信息以及域的声明顺序,域的相关信息包括:
域名
域类型
域修饰符(public, private, protected,static,final
        volatile, transient的某个子集)
       
方法信息
jvm必须保存所有方法的以下信息,同样域信息一样包括声明顺序
方法名
方法的返回类型(或 void)
方法参数的数量和类型(有序的)
方法的修饰符(public, private, protected, static, final,
           synchronized, native, abstract的一个子集)
除了abstract和native方法外,其他方法还有保存
方法的字节码(bytecodes)
操作数栈和方法栈帧的局部变量区的大小           
异常表

类变量(
  Class Variables
  译者:就是类的静态变量,它只与类相关,所以称为类变量
)
类变量被类的所有实例共享,即使没有类实例时你也可以访问它。这些变量只与类相关,所以在方法区中,它们成为类数据在逻辑上的一部分。在jvm使用一个类之前,它必须在方法区中为每个non-final类变量分配空间。

常量(被声明为final的类变量)的处理方法则不同,每个常量都会在常量池中有一个拷贝。non-final类变量被存储在声明它的类信息内,而final类被存储在所有使用它的类信息内。

 

对类加载器的引用
jvm必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么jvm会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。jvm在动态链接的时候需要这个信息。当解析一个类型到另一个类型的引用的时候,jvm需要保证这两个类型的类加载器是相同的。这对jvm区分名字空间的方式是至关重要的。

 

对Class类的引用
jvm为每个加载的类型(译者:包括类和接口)都创建一个java.lang.Class的实例。而jvm必须以某种方式把Class的这个实例和存储在方法区中的类型数据联系起来。你可以通过Class类的一个静态方法得到这个实例的引用// A method declared in class java.lang.Class:
public static Class forName(String className);假如你调用forName("java.lang.Object"),你会得到与
java.lang.Object对应的类对象。你甚至可以通过这个函数得到任何包中的任何已加载的类引用,只要这个类能够被加载
到当前的名字空间。如果jvm不能把类加载到当前名字空间,forName就会抛出ClassNotFoundException。
(译者:熟悉COM的朋友一定会想到,在COM中也有一个称为类对象(Class Object)的东东,这个类对象主要 是实现一种工厂模式,而java由于有了jvm这个中间层,类对象可以很方便的提供更多的信息。这两种类对象都是Singleton的)

也可以通过任一对象的getClass()函数得到类对象的引用,getClass被声明在Object类中:// A method declared in class java.lang.Object:
public final Class getClass();
例如,假如你有一个java.lang.Integer的对象引用,可以激活getClass()得到对应的类引用。通过类对象的引用,你可以在运行中获得相应类存储在方法区中的类型信息,下面是一些Class类提供的方法:
// Some of the methods declared in class java.lang.Class:
public String getName();
public Class getSuperClass();
public boolean isInterface();
public Class[] getInterfaces();
public ClassLoader getClassLoader();

这些方法仅能返回已加载类的信息。getName()返回类的完整名,getSuperClass()返回父类的类对象,isInterface()判断是否是接口。getInterfaces()返回一组类对象,每个类对象对应一个直接父接口。如果没有,则返回一个长度为零的数组。getClassLoader()返回类加载器的引用,如果是由启动类加载器加载的则返回null。所有的这些信息都直接从方法区中获得。

方法表
为了提高访问效率,必须仔细的设计存储在方法区中的数据信息结构。除了以上讨论的结构,jvm的实现者还可以添加一些其他的数据结构,如方法表。jvm对每个加载的非虚拟类的类型信息中都添加了一个方法表,方法表是一组对类实例方法的直接引用(包括从父类继承的方法)。jvm可以通过方法表快速激活实例方法。(译者:这里的方法表与C++中的虚拟函数表一样,但java方法全都是virtual的,自然也不用虚拟二字了。正像java宣称没有指针了,其实java里全是指针。更安全只是加了更完备的检查 机制,但这都是以牺牲效率为代价的,个人认为java的设计者 始终是把安全放在效率之上的,所有java才更适合于网络开发)

一个例子
为了显示jvm如何使用方法区中的信息,我们据一个例子,我们
看下面这个类:
class Lava {
    private int speed = 5; // 5 kilometers per hour
    void flow() {
    }
}

class Volcano {
    public static void main(String[] args) {
        Lava lava = new Lava();
        lava.flow();
    }
}
下面我们描述一下main()方法的第一条指令的字节码是如何被执行的。不同的jvm实现的差别很大,这里只是其中之一。

为了运行这个程序,你以某种方式把“Volcano"传给了jvm。有了这个名字,jvm找到了这个类文件(Volcano.class)并读入,它从类文件提取了类型信息并放在了方法区中,通过解析存在方法区中的字节码,jvm激活了main()方法,在执行时,jvm保持了一个指向当前类(Volcano)常量池的指针。注意jvm在还没有加载Lava类的时候就已经开始执行了。正像大多数的jvm一样,不会等所有类都加载了以后才开始执行,它只会在需要的时候才加载。main()的第一条指令告知jvm为列在常量池第一项的类分配足够的内存。jvm使用指向Volcano常量池的指针找到第一项,发现是一个对Lava类的符号引用,然后它就检查方法区看lava是否已经被加载了。这个符号引用仅仅是类lava的完整有效名”lava“。这里我们看到为了jvm
能尽快从一个名称找到一个类,一个良好的数据结构是多么重要。这里jvm的实现者可以采用各种方法,如hash表,查找树等等。同样的算法可以用于Class类的forName()的实现。当jvm发现还没有加载过一个称为"Lava"的类,它就开始查找并加载类文件"Lava.class"。它从类文件中抽取类型信息并放在了方法区中。jvm于是以一个直接指向方法区lava类的指针替换了常量池第一项的符号引用。以后就可以用这个指针快速的找到lava类了。而这个替换过程称为
常量池解析(constant pool resolution)。在这里我们替换的是一个native指针。jvm终于开始为新的lava对象分配空间了。这次,jvm仍然需要方法区中的信息。它使用指向lava数据的指针(刚才指向volcano常量池第一项的指针)
找到一个lava对象究竟需要多少空间。jvm总能够从存储在方法区中的类型信息知道某类型对象需要的空间。但一个
对象在不同的jvm中可能需要不同的空间,而且它的空间分布也是不同的。(译者:这与在C++中,不同的编译器也有不同的对象模型是一个道理)一旦jvm知道了一个Lava对象所要的空间,它就在堆上分配这个空间并把这个实例的变量speed初始化为缺省值0。假如lava的父对象也有实例变量,则也会初始化。当把新生成的lava对象的引用压到栈中,第一条指令也结束了。下面的指令利用这个引用激活java代码把speed变量设为初始值,5。另外一条指令会用这个引用激活
Lava对象的flow()方法。

分享到:
评论

相关推荐

    JVM内核架构--JVM规范

    Method area: holds the details of each class loaded by the class loader subsystem. Heap: holds every object being created by the threads during execution Thread specific runtime data areas: Program ...

    06.JVM原理讲解和调优.pdf

    4. 方法区(Method Area):存放类的元数据和方法信息。 JVM 的垃圾回收机制: 1. MARK:标记垃圾对象。 2. SWEEP:清除垃圾对象。 3. COMPACT:压缩堆空间。 五、JVM 的性能优化 JVM 的性能优化可以从以下几个...

    深入详解JVM内存模型与JVM参数详细配置.pdf

    knowledge point 3: 方法区(Method Area) 方法区也称“永久代”,用于存储虚拟机加载的类信息、常量、静态变量,是各个线程共享的内存区域。从JDK8开始,方法区不再是永久代,而是Metaspace。 knowledge point ...

    个人对JVM总结图.png

    个人对JVM的总结图。包括JVM的五部分(Program Counter Register、JVM Stack、Native Method Stack、heap、method area)。欢迎各位网友指正错误,互相学习讨论

    java技术面试必问:JVM-内存模型讲解.docx

    JVM内存模型共分为5个区:堆(Heap)、方法区(Method Area)、程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)。其中,堆(Heap)、方法区(Method Area)为...

    一篇文章掌握整个JVM,JVM超详细解析!!!

    JVM先想想一些问题1 我们开发人员编写的Java代码是怎么让电脑认识的2 为什么说java是跨平台语言3 Jdk和Jre和JVM的区别4 为什么要学习JVM深入学习JVM1 JVM运行时数据区2 解析JVM运行时数据区2.1 方法区(Method Area...

    java jvm内存模型

    5. 方法区(Method Area) 方法区是所有线程共享的数据区,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区中还存储了每个类的信息,如类的名称、字段、方法等。 JVM...

    2020程序员必看这份JVM大厂高频面试题与知识点整合!.pdf,这是一份不错的文件

    5. 方法区(Method Area):存储类信息、字段信息、方法信息等。 三、执行引擎 执行引擎是JVM的组成部分,负责将字节码文件执行成机器码。执行引擎的主要任务是将字节码文件解释成机器码,并执行机器码。 四、GC...

    Java虚拟机 JVM 内存结构介绍

    主要介绍Runtime Data Area,包括Java Stack,Native Method Stack, Program Counter Register,Method Area以及Heap 还简要介绍了Runtime Data Area周边的模块,包括Class Loader,Execution Engine,Native ...

    JVM原理及内存溢出案列分析PPT教案学习.pptx

    * 方法区(Method Area):有点像以前常说的“进程代码段”,这里面存放了每个加载类的反射信息、类函数的代码、编译时常量等信息。 * 原生方法栈(Native Method Stack):主要用于JNI中的原生代码,平时很少涉及。...

    java8rt.jar源码-jvm:jvm入门jvm面试题

    Area(方法区)、Heap(堆)、Program Counter Register(程序计数器)、JAVA Method Stack(JAVA方法栈)、Native Method Stack(本地方法栈)。 3、类加载器 对象实例化过程 类加载器类别 BootstrapClassLoader(启动类加载...

    超硬核!!!一篇文章搞定整个JVM运行时数据区

    JVM运行时数据区1 JVM运行时数据区2 解析JVM运行时数据区2.1 方法区(Method Area)2.2 Java堆(Java Heap)2.3 程序计数器(Program Counter Register)2.4 Java虚拟机栈(Java Virtual Machine Stacks)2.5 本地...

    Java核心知识点整理.pdf

    5. 方法区/永久代(Method Area/PermGen):方法区/永久代是JVM的内存区域之一,用于存储类和方法的信息。 JVM运行时内存 JVM运行时内存是JVM的内存管理机制,负责管理Java对象和数组的生命周期。JVM运行时内存...

    笔记,1、虚拟机的前世今生,深入理解JVM内存区域1

    4. 方法区(Method Area):用于存储已经被虚拟机加载的类信息,常量("zdy","123"等),静态变量(static 变量)等数据,可用以下参数调整:jdk1.7 及以前:-XX:PermSize;-XX:MaxPermSize;jdk1.8 以后:-XX:Metaspace...

    字节大佬总结的Java面试资料.pdf

    * 方法区/永久代(Method Area/PermGen):用于存储类信息、常量、静态变量等,所有线程共享。 JVM 运行时内存 JVM 运行时内存可以分为以下几个部分: * 新生代(Young Generation):用于存储新生的对象,包括 ...

    15 - ASM之方法Frame - 简书1

    其中,Runtime Data Areas包括Method Area、Heap Area、Stack Area、PC Registers和Native Method Stack等部分。 JVM Stack 在JVM Architecture中,每个线程(Thread)都对应一个属于自己的JVM Stack。当一个新...

    JAVA面试核心知识点整理(283页).pdf

    * 方法区/永久代(Method Area/Perm Gen):方法区/永久代是所有线程共享的内存区域,用于存储类的元数据、字段和方法信息。 JVM运行时内存 JVM的运行时内存主要分为新生代和老年代两个区域。 * 新生代(Young ...

    虚拟机学习笔记--周志明老师第三版

    * 方法区(Method Area):存储被 JVM 加载的类信息、运行时常量池、JIT 编译后的 Code Cache 等信息。 * 直接内存(Direct Memory):用于 NIO 的缓冲区分配,避免在系统内存与 JVM 堆内存之间拷贝的开销。 * 线程...

    借HSDB来探索HotSpot VM的运行时数据1

    DR版回答是:t1在存Java静态变量的地方, 概念上在JVM的方法区(method area)里t2在Java堆里, 作为Test的一个实例的字段存在t3在J

Global site tag (gtag.js) - Google Analytics