JVM内存分配
- 程序计数器Program Counter Register
- 本地方法栈Native Method Stack
- 方法区Method Area
- 虚拟机栈VM Stack
- 堆Heap
-
程序计数器
- 供CPU使用 本地方法栈
- 为虚拟机使用的native方法使用 方法区
- 存储已被虚拟机加载的 类信息、 常量、 静态变量、即时编译器编译后的代码等数据 运行时常量池Runtime Constant Pool
- 是方法区的一部分,用于存放编译期生成的各种字面量和符号引用 虚拟机栈
- 每个方法被执行的时候都会同时创建一个栈帧Stack Frame用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 堆
- 存放对象实例
类的加载、连接与初始化
类的加载
-
概念
- 通过类加载器ClassLoader查找并加载类的二进制数据(.class文件),先是将其数据结构加载到方法区,然后在堆内存中创建相应的class文件,用于封装方法区中的数据结构,还有提供给java程序员调用的接口
类被加载后就进入连接阶段
类的连接
- 验证:确保被加载类的正确
- 准备:为类的静态变量分配内存,并将其初始化为默认值
- 解析:把类中的符号引用转化为直接引用
类的初始化
-
概念
- 为类 静态变量赋予正确的初始值,即显示初始化
在类被java程序“首次主动使用”时才会被初始化
主动使用(6种)
- 创建类的实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射,如
Class.forName("com.mysql.jdbc.Driver")
- 初始化一个类的子类
- java虚拟机启动时被标明为启动类的类
类的初始化步骤
- 假如这个类还没有被加载和连接,那就先进行加载和连接
- 假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接的父类。不适用于接口。
- 假如类中存在初始化语句,那就依次执行这些初始化语句。静态变量的声明语句,和静态代码块都被看做类的初始化语句。
三个例子
demo1
public class Demo1 {
//请切换1、2、3处代码
public static final int a = 0;//1.没有进入静态代码块
//public static int a = 0;//2.进入静态代码块
//public static final int a = new Random().newInt(100);//3.进入静态代码块
static {
System.out.println("进入静态代码块了");
}
}
//static final是常量,解析的时候已经赋值了,就不用初始化类了。
//第3种情况是只有在运行时才能确定随机数,所以不是编译时的常量,
//也就是个变量,所以被调用的时候会导致该类初始化
demo2
class Dad {
static int a = 3;
static {
System.out.println("dad");
}
}
class Son extends Dad {
static {
System.out.println("son");
}
}
public class Demo2 {
public static void main(String[] args) {
System.out.println(Son.a);
}
}
//输出dad\r\n3
//只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,
//才可以认为是对该类或接口的直接使用
demo3
class Singleton {
//请切换1、2处代码
private static Singleton singleton = new Singleton();//情况1
public static int count1;
public static int count2 = 0;
//private static Singleton singleton = new Singleton();//情况2
private Singleton() {
count1++;
count2++;
}
public static Singleton getInstance() {
return singleton;
}
}
public class Demo3 {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println("count1="+singleton.count1);
System.out.println("count2="+singleton.count2);
}
}
调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化
加载器
- 根(Boostrap)类加载器:负责加载虚拟机的核心类库。
- 扩展(Extension)类加载器:从java.ext.dirs系统属性所制定的目录中加载类库,或者从JDK的安装目录的jre\lib\ext子目录(扩展目录)下加载类库。
- 系统(System)类加载器:也称为应用类加载器。从环境变量classpath或者系统属性java.class.path所制定的目录中加载类,是用户自定义的类加载器的默认父加载器。
父亲委托机制
从JDK 1.2版本开始,类的加载过程采用父类委托机制,这种机制能更好地保证java平台的安全。在此委托机制中,除了java虚拟机自带的根类加载器以外,其余的类加载器都有且只有一个父加载器(不是类之间的继承关系,是加载器对象之间的包装关系)。当java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载器能加载,则有父加载器完成加载任务,否则才有加载器loader1本身加载Sample类。
java对象的创建过程
使用new关键字创建对象,在堆中给对象分配内存空间
给对象所属类中的所有非静态成员变量分配空间并进行默认的初始化
执行和new对象时传递参数一致的构造函数
- 执行super(),找父类的空参数构造函数
- 给成员变量进行显示赋值
- 执行构造代码块
- 当然,构造函数的方法也是要跑的
构造函数执行完成,对象创建结束