Java的内存需要划分成为5个部分:
- 栈(Stack):存放的都是方法中的局部变量
- 局部变量:方法的参数,或者是方法{}内部的变量
- 作用域:一旦超出作用域,立刻从栈内存当中消失。
- 堆内存(Heap):凡是new出来的东西,都在堆当中。
-
堆内存里面的东西都有一个地址值:16进制
-
堆内存里面的数据,都有默认值。规则:
-
如果是整数 默认0
-
如果是整数类型,那么默认为0;
-
如果是浮点类型,那么默认为0.0;
-
如果是字符类型,那么默认为’\u0000′;
-
如果是布尔类型,那么默认为false;
-
如果是引用类型,那么默认为null。
-
随着垃圾回收(GC)机制而消失。
- 方法区(Method Area):存储.class相关信息,包含方法的信息。
-
类型信息:
-
类的完整名称(比如,java.long.String)
-
类的直接父类的完整名称
-
类的直接实现接口的有序列表(因为一个类直接实现的接口可能不止一个,因此放到一个有序表中)
-
类的修饰符可以看做是,对一个类进行登记,这个类的名字叫啥,他粑粑是谁、有没有实现接口, 权限是啥;
-
类型的常量池:
-
每一个Class文件中,都维护着一个常量池(这个保存在类文件里面,不要与方法区的运行时常量池搞混),里面存放着编译时期生成的各种字面值和符号引用;这个常量池的内容,在类加载的时候,被复制到方法区的运行时常量池 ;
-
字面值:就是像string, 基本数据类型,以及它们的包装类的值,以及final修饰的变量,简单说就是在编译期间,就可以确定下来的值;
-
符号引用:不同于我们常说的引用,它们是对类型,域和方法的引用,类似于面向过程语言使用的前期绑定,对方法调用产生的引用;
-
存在这里面的数据,类似于保存在数组中,外部根据索引来获得它们 ;
-
字段信息:
-
声明的顺序
-
修饰符
-
类型
-
名字
-
方法信息:
-
声明的顺序
-
修饰符
-
返回值类型
-
名字
-
参数列表(有序保存)
-
异常表(方法抛出的异常)
-
方法字节码(native、abstract方法除外,)
-
操作数栈和局部变量表大小
-
类变量:
-
非final类变量
-
在java虚拟机使用一个类之前,它必须在方法区中为每个非final类变量分配空间。非final类变量存储在定义它的类中;
-
final类变量(不存储在这里)
-
由于final的不可改变性,因此,final类变量的值在编译期间,就被确定了,因此被保存在类的常量池里面,然后在加载类的时候,复制进方法区的运行时常量池里面 ;final类变量存储在运行时常量池里面,每一个使用它的类保存着一个对其的引用;
-
指向类加载器的引用:
jvm必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么jvm会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。 -
指向class实例的引用:
jvm为每个加载的类都创建一个java.lang.Class的实例(存储在堆上)。而jvm必须以某种方式把Class的这个实例和存储在方法区中的类型数据(类的元数据)联系起来, 因此,类的元数据里面保存了一个Class对象的引用; -
方法表:
-
为了提高访问效率,必须仔细的设计存储在方法区中的数据信息结构。除了以上讨论的结构,jvm的实现者还可以添加一些其他的数据结构,如方法表。jvm对每个加载的非虚拟类的类型信息中都添加了一个方法表,方法表是一组对类实例方法的直接引用(包括从父类继承的方法。jvm可以通过方法表快速激活实例方法。(译者:这里的方法表与C++中的虚拟函数表一样,但java方法全都是virtual的,自然也不用虚拟二字了。正像java宣称没有指针了,其实java里全是指针。更安全只是加了更完备的检查机制,但这都是以牺牲效率为代价的,个人认为java的设计者 始终是把安全放在效率之上的,所以java才更适合于网络开发)
- 本地方法栈(Native Method Stack):与操作系统相关。
- 寄存器(pc Register):与CPU相关。