JVM之方法区
方法区
先说特点
1、方法区(Method Area)与 Java 堆一样,是所有线程共享的内存区域。
2、Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。其实也好理解,就是一些class类型的对象;比如Object.class,这也是个对象;只是个对象不是要用的对象,是模具对象,用来制造真正需要的对象的对象;就比如制作月饼需要模子,月饼是对象,模子也是对象;这是模子不是存放在仓库里(堆),而且存在加工厂(方法区),本质来讲都是存储对象的地方,只是方法区需要事先预备好;
3、因为需要事先预备好,所以类通过类加载器,把字节码class文件,加载到方法区中,就变成了真正的模子,我们写好的.java文件经过编译后只能算是一张模子的图纸,只有经过classloader加载之后才能变成真正的能制造对象的模子;
4、静态成员即类变量,存储在field_info表中;静态方法存储在method_info表中;看对象存储在哪要看怎么写,比如下面的
//这行代码就创建了两个对象,一个是"str",一个是new String();可能有人看不懂,下面举个例子你就明白了
String str = new String("str");
//第二个例子,我们的String特殊就特殊在可以直接写出对象来,和八个基本数据类型一样,都可以直接写出来,不要声明类型啊,然后new啊什么的,所以给构造方法传入的"str"自然也是个对象,是在常量池中产生的
String str = new String(new Object().toString());
classload加载的类的内容
一 、类型信息
- 类型的全限定名 2. 超类的全限定名 3. 直接超接口的全限定名 4. 类型标志(该类是类类型还是接口类型) 5. 类的访问描述符(public、private、default、abstract、final、static)
二、类型的常量池
存放该类型所用到的常量的有序集合,包括直接常量(如字符串、整数、浮点数的常量)和对其他类型、字段、方法的符号引用。常量池中每一个保存的常量都有一个索引,就像数组中的字段一样。因为常量池中保存中所有类型使用到的类型、字段、方法的符号引用,所以它也是动态连接(栈中对应的方法指向这个引用)的主要对象(在动态链接中起到核心作用)。
三、字段信息 - 字段修饰符(public、protect、private、default) 2. 字段的类型 3. 字段名称
四、方法信息 - 方法名 2.方法的返回类型(包括void)3. 方法参数的类型、数目以及顺序 4. 方法修饰符(public、private、protected、static、final、synchronized、native、abstract) 5. 针对非本地方法,还有些附加方法信息需要存储在方法区中(局部变量表大小和操作数栈大小、方法体字节码、异常表)
五、类变量(静态变量)
指该类所有对象共享的变量,即使没有创建该对象实例,也可以访问的类变量。它们与类进行绑定
六、指向类加载器的引用
JVM 必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么 JVM 会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。JVM 在动态链接的时候需要这个信息。当解析一个类型到另一个类型的引用的时候,JVM 需要保证这两个类型的类加载器是相同的。这对 JVM 区分名字空间的方式是至关重要的。
七、指向 Class 实例的引用
JVM 为每个加载的类和接口都创建一个 java.lang.Class 实例(JDK6 存储在方法区,JDK6 之后存储在 Java 堆),这个对象存储了所有这个字节码内存块的相关信息,如平时使用的 this.getClass().getName() this.getClass().getDeclaredMethods() this.getClass().getDeclaredFields(),可以获取类的各种信息,都是通过这个 Class 引用获取。
八、方法表
为了提高访问效率,必须仔细的设计存储在方法区中的数据信息结构。除了以上讨论的结构,JVM 的实现者还可以添加一些其他的数据结构,如方法表。JVM 对每个加载的非虚拟类的类型信息中都添加了一个方法表,方法表是一组对类实例方法的直接引用(包括从父类继承的方法)。JVM 可以通过方法表快速激活实例方法。(这里的方法表与 C++ 中的虚拟函数表一样。正像 Java 宣称没有指针了,其实 Java 里全是指针。更安全只是加了更完备的检查机制,但这都是以牺牲效率为代价的,Java 的设计者始终是把安全放在效率之上的,所有 Java 才更适合于网络开发)
常量池和运行时常量池
public static String a = "a";
public static String b = "b";
public static String a_b = "a" + "b";
public static String a_b_ = "a" + "b";
public static String ab = a + b;
public static String ab2 = a + b;
public static void main(String[] args) {
System.out.println(a_b == a_b_);//true 常量池
System.out.println(ab == ab2);//false 运行时常量池
}
通过代码和我们就可以看出,赋值的时候,如果是变量相加,那么就是运行时常量,结果就是false;如果有变是两个确定的String对象,那就是true,就是常量池