浅谈 java程序运行时内存分配及运行机制
第一部分:内存分配
一、 基本概念
1.每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,
2.每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一对应一个堆,该进程的所有线程共享这个堆
3.jvm为每一个线程分配一个栈,单个线程独享栈。
4.Java中分配堆内存是自动初始化的,引用存放于栈中,对象存放于堆中,引用包含着指向堆区的指针
5.当变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。
二.java内存分区
l 栈:保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。
l 堆:用来存放对象及对象中的非静态的成员变量,对象所属类中的方法不在堆中。
| 方法区(静态区):
方法区存储内容有:
1.类的类型信息
1.1 此类型的完整有效名
1.2 此类型直接父类的完整有效名(除非这个类型是interface或是 java.lang.Object,两种情况下都没有父类)
1.3 此类型的修饰符(public,abstract, final的某个子集)
1.4 此类型直接接口的一个有序列表
2.类型的常量池( constant pool):jvm为每个已加载的类型都维护一个常量池。常量池就是这个类型用到的常量的一个有序集合,包括实际的常量(string, integer, 和 floating point常量)和对类型,域和方法的符号引用。池中的数据项象数组项一样,是通过索引访问的。
因为常量池存储了一个类型所使用到的所有类型,域和方法的符号引用,所以它在java程序的动态链接中起了核心的作用
3.域信息 :jvm必须在方法区中保存类型的所有域的相关信息以及域的声明顺序,
3.1 域名 3.2 域类型 3.3 域修饰符(public, private, protected,static,final volatile, transient的某个子集)
4.方法信息
jvm必须保存所有方法的以下信息:
4.1方法名
4.2 方法的返回类型(或 void)
4.3 方法参数的数量和类型(有序的)
4.4 方法的修饰符(public, private, protected, static, final, synchronized, native, abstract的一个子集)除了abstract和native方法外,其他方法还有保存方法的字节码(通过 解析字节码调用方法)操作数栈和方法栈帧的局部变量区的大小
5.异常表
6.类变量:及静态变量
三:小结:
1.类的静态变量属于类,存在方法区。类的成员变量属于对象,存于堆。方法中的局部变量存于栈。
2.静态方法属于类,非静态方法属于对象。
3.对象是类的一个实例,类是对象的抽象集合。因此对象可以使用类方法和类变量,对象层面的方法(非静态方法)也可以使用类成员变量,
与之相反的是:类无法使用非静态成员变量和非静态方法,类层面的方法(静态方法)也无法使用非静态类型的成员变量。
第二部分:运行过程分析
类的加载时机:
类加载过程:
java程序经过编译后形成*.class文件,内含JVM的字节码。通过类加载器将字节码(*.class)加载入JVM的内存中。类加载过程主要涉及JVM的方法区。JVM将类加载过程分成加载,连接,初始化三个阶段,其中连接阶段又细分为验证,准备,解析三个阶段。通常把加载和初始化简化成为一个过程,因为加载就会引起初始化。
需要初始化的部分:
1)静态成员变量
2)非静态成员变量
3)静态代码块:用于给类初始化,类加载时就会被加载执行,只执行一次。
4)非静态代码块:用于给对象初始化,只要建立对象该部分都会执行,优先于构造函数执行,每个对象只执行一次。
构造函数:给对象的对象初始化,建立对象是,选择相应的构造函数初始化对象
5)构造器
类的部分加载机制:
a.当通过类方式(类加载时机部分的(2)(4)(5))进行类的加载时:仅会加载静态成员变量,静态代码块
b.当通过对象方式(类加载时机部分的(1))进行类的加载时:所有需要初始化的部分均要初始化
对象方式加载类的初始化顺序:
2.子类静态成员和静态初始化块,按在代码中出现的顺序依次初始。
3. 父类的实例成员和实例初始化块,按在代码中出现的顺序依次初始。
4.初始父类的构造方法。
5.子类实例成员和实例初始化块,按在代码中出现的顺序依次执行。
6.初始子类的构造方法。
通过test类解释上述知识:
public class test {
int t;
static{
int i=6;
System.out.println(i);
}
{ int f=9;
System.out.println(f);
}
public test() {
// TODO Auto-generated constructor stub
t=8;
System.out.println(t);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("test");
new test();
}
}
1.出现mian方法,开始用类方式加载这个类,并加载到方法区,因为时类方式所以仅会初始static块,此时打印6
2.初始化完毕,继续执行main方法,执行System.out语句(也加载了system类)打印test
3.继续执行main方法,出现new test();进行对象方式的类加载,并加载到方法区,这次加载会初始化普通块,打印9,然后初始构造器,打印8.初始完成,将对象存入堆中。
4.mian执行结束,程序结束