首先要说明的是,此文章转载自

http://blog.csdn.net/zzhangxiaoyun/article/details/7518917

  谢谢作者。是本人认为对方法区解释得比较清楚的一篇文章了。

  另外,这里ps一下,Class对象是存放在堆区的,不是方法区!这点很多人容易犯错。类的元数据(元数据并不是类的Class对象!Class对象是类加载的最终产品,类的方法代码,变量名,方法名,访问权限,返回值等等都是在方法区的)才是存在方法区的!Class对象可以认为是类的反射对象,所有的反射相关的操作就是由它而起。Class对象在一个类被加载完成的时候生成。

  方法区

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

  jvm实现的设计者决定了类型信息的内部表现形式。如,多字节变量在类文件是以big-endian存储的,但在加载到方法区后,其存放形式由jvm根据不同的平台来具体定义。

jvm在运行应用时要大量使用存储在方法区中的类型信息。在类型信息的表示上,设计者除了要尽可能提高应用的运行效率外,还要考虑空间问题。根据不同的需求,jvm的实现者可以在时间和空间上追求一种平衡。

  因为方法区是被所有线程共享的,所以必须考虑数据的线程安全。假如两个线程都在试图找lava的类,在lava类还没有被加载的情况下,只应该有一个线程去加载,而另一个线程等待。

  方法区的大小不必是固定的,jvm可以根据应用的需要动态调整。同样方法区也不必是连续的。方法区可以在堆(甚至是虚拟机自己的堆)中分配。jvm可以允许用户和程序指定方法区的初始大小,最小和最大尺寸。

  方法区同样存在垃圾收集,因为通过用户定义的类加载器可以动态扩展java程序,一些类也会成为垃圾。jvm可以回收一个未被引用类所占的空间,以使方法区的空间最小。

  类型信息

  对每个加载的类型,jvm必须在方法区中存储以下类型信息:

  一 这个类型的完整有效名

  二 这个类型直接父类的完整有效名(除非这个类型是interface或是

java.lang.Object,两种情况下都没有父类。更正下,inteface接口应该也可以是有父类的。)

  三 这个类型的修饰符(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的类变量,即final static)的处理方法则不同,每个常量都会在常量池中有一个拷贝。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()方法。

(转)java中的方法区的更多相关文章

  1. 从几个sample来学习JAVA堆、方法区、JAVA栈和本地方法栈

    最近在看<深入理解Java虚拟机>,书中给了几个例子,比较好的说明了几种OOM(OutOfMemory)产生的过程,大部分的程序员在写程序时不会太关注Java运行时数据区域的结构: 感觉有 ...

  2. java虚拟机 jvm 方法区实战

    和java堆一样,方法区是一块所有线程共享的内存区域,用于保存系统的类信息,类的信息有哪些呢.字段.方法.常量池.方法区也有一块内存区域所以方法区的内存大小,决定了系统可以包含多少个类,如果系统类太多 ...

  3. Class实例在堆中还是方法区中?

    1.JVM中OOP-KLASS模型 在JVM中,使用了OOP-KLASS模型来表示java对象,即:1.jvm在加载class时,创建instanceKlass,表示其元数据,包括常量池.字段.方法等 ...

  4. Java JVM——9.方法区

    前言 方法区是运行时数据区的最后一个部分: 从线程共享与否的角度来看: 大家可能在这里有些疑惑,方法区和元空间的关系到底是怎样的?请往下看,下面会为大家解惑. 栈.堆.方法区的交互关系 下面就涉及了对 ...

  5. JVM中的方法区

    JVM中的方法区 方法区存储什么? 用于存储已被虚拟机加载的类型信息.常量.静态变量.即时编译器编译后的代码缓存 1.类型信息 对每个加载的类型(类class.接口interface.枚举.注解)jv ...

  6. Java中的方法应用

    一.如何定义java中的方法 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 语法: 1. 访问修饰符:方法允许被访问的权限范围, 可以是 public.protected.priv ...

  7. c#和java中的方法覆盖——virtual、override、new

    多态和覆盖 多态是面向对象编程中最为重要的概念之一,而覆盖又是体现多态最重要的方面.对于像c#和java这样的面向对象编程的语言来说,实现了在编译时只检查接口是否具备,而不需关心最终的实现,即最终的实 ...

  8. Java中的方法(形参及实参)return返回类型

    如何定义 Java 中的方法 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 一般情况下,定义一个方法的语法是: 其中: 1. 访问修饰符:方法允许被访问的权限范围, 可以是 pub ...

  9. java中的方法method

    java中的方法必须存在于类class里,不能独立存在.类是描述具有某种特征的事物,方法则是这类 事物具有的某种功能,通过调用方法可以实现某种特定的功能.方法名一般以小写的动词开头. 例: publi ...

随机推荐

  1. Vue组件库 VV-UI 开始接受PR啦,有兴趣小伙伴可以一起参与开源哦。

    前言: 刚开源出来的VV-UI目前刚刚起步,组件不是很多,非常欢迎大家的pr和Star.项目地址: https://github.com/VV-UI/VV-UI演示地址: https://vv-ui. ...

  2. fragmentTabHost 使用示例

    目前我们看微信的底部,有四个导航栏,那我们应该来怎么实现类似的导航栏呢? 在 android 4.0 的时候,推出了一个新的工具,fragmentTabHost . fragmentTabHost  ...

  3. MarkdownPad2测试

    IO利用率统计 SAS SSD MegaRaid

  4. python 自动拉起进程脚本

    cat /usr/local/ssdb/moniter_ssdb.py #!/usr/bin/env python import os import sys import commands #ssdb ...

  5. linux如何在日志中查找关键字、前几行、结尾几行

    如何使用命令行快速查看项目日志是每个开发人员必备技能,尤其在没有专门日志搜集系统的情况下,想要知道目前项目运行状态最好的办法就是打开log日志一瞅即明白. 复杂的到用时再查不晚,但是简单的还是有必要掌 ...

  6. 关于事件mouseover ,mouseout ,mouseenter,mouseleave的区别

    轮播中大多会选择mouseover和mouseout  这个时候是没有任何问题的 但当遇到有css3动画的时候,会发现移入移出过快 动画还没加载完成就需要执行下一个动画,完了动画样式就错乱了. 这时候 ...

  7. HTML Entity 字符实体

    目录 1. HTML Entity 2. 字符与Entity Name的互相转换 3. 字符与Entity Number的互相转换 1. HTML Entity 1.1 介绍 在编写HTML页面时,需 ...

  8. CentOS6.x机器安装Python2.7.x

    准备环境:CentOS6.9机器 1.查看机器默认的Python版本 [root@hlmcent69nma ~]# python -V Python [root@hlmcent69nma ~]# wh ...

  9. 免费人脸识别APi

    今天对应一些免费的人脸识别的api 做了一下简单的对比,觉得百度开发出来的人脸识别接口还是最符合的我的要求,简单易用,容易上手. 据说百度的一些门禁也使用上了人脸识别的功能了,功能很强大,而且能识别出 ...

  10. 【Mysql知识补充】

    一.子查询 1.定义 子查询是将一个查询语句嵌套在另一个查询语句中.内层查询语句的查询结果,可以为外层查询语句提供查询条件.子查询中可以包含:IN.NOT IN.ANY.ALL.EXISTS 和 NO ...