java内存分析:
类的加载过程:
- 类的加载(Load) 类加载器完成将类的class文件读入内存并创建java.lang.Class对象操作。
- 类的链接(Link) 将类的二进制数据合并进JRE,在合并的过程中可以对类进行校验,检查其是否存在安全问题,是否符合JVM语法规范,接着为类变量(static)分配内存和设置默认初始值,这些内存在方法区中进行分配。最后在虚拟机中将常量名替换为引用地址。
- 类的初始化(Initialize) JVM对类进行初始化,过程中执行类构造器的方法,此方法是编译期自动收集类中的变量赋值动作和静态代码合并而成的,且虚拟机会保证类构造器的方法会在多线程中被正确的加锁和同步。且在初始化过程中,如果发现类的父类还没有被初始化,则会优先初始化其父类。
样例代码:
public class test04 {
/**
* 1.加载到内存,产生一个java.lang.Class对象
* 2.链接,结束后m=0,static默认值为0
* 3.初始化,类构造器中有一个clinit方法,方法对赋值动作和代码进行合并
* clinit(){
* m=300
* m=100
*
* }
*/
public static void main(String[] args) {
A a = new A();
System.out.println(A.sum);
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
sum = 300;
}
static int sum =100;
public A() {
System.out.println("A类的无参构造初始化");
}
}
内存图:
会发生类初始化的情况:(类的主动引用)
1.虚拟机启动,首先初始化main方法所在的类
2.new一个类对象的时候
3.调用类静态成员(非final)和静态方法
4.使用java.lang.reflect包的方法对类进行反射调用时
5.初始化一个类,如果其父类没有被初始化,则优先初始化其父类
不会发类初始化(类的被动引用)
1.当访问哪一个静态域时,只有真正声明这个域的类才被初始化
2.通过数组定义类引用时
3.引用常量不会触发初始化
样例:
package Reflection;
public class test05 {
static {
System.out.println("主类被加载");
}
//测试类什么时候回初始化
public static void main(String[] args) throws ClassNotFoundException {
//主动引用
//Son son = new Son();
//反射产生主动引用
//Class c1 = Class.forName("Reflection.Son");
/*<<<<<<<<<<不会产生类的引用的方法<<<<<<<<<<*/
//子类调用父类的静态
//System.out.println(Son.b);
//数组定义类引用,此处仅仅是空间声明,不是类声明
//Son[] array = new Son[5];
//调用常量池中内容
System.out.println(Son.M);
}
}
class Father{
static int b=2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 4;
}
类加载器的作用:
类加载的作用:将class文件字节码内容加载到内存,并将这些静态数据转换成方法区运行时的数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中数据的访问入口
类缓存:javaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将缓存一端时间。但JVM的垃圾回收机制可以回收这些Class对象
类加载器的种类:
引导类加载器:C++编写,JVM自带加载器,负责java核心库,无法直接获取。
扩展类加载器:负责jre/lib/ext目录下的jar包
系统类加载器:负责java-classpath或者-D java.class.path所指的目录下的类与jar包装入工作,最常见的加载器
样例:
package Reflection;
public class test06 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemclassloader = ClassLoader.getSystemClassLoader();
System.out.println(systemclassloader);
//获取系统类加载器的父类加载器 -> 扩展类加载器
ClassLoader parentloader = systemclassloader.getParent();
System.out.println(parentloader);
//获取 扩展类加载器的父类加载器 -> 根加载器
ClassLoader rootloader = parentloader.getParent();
//但因为根加载器是由c/c++编写,java无法读取,所以返回输出为null
System.out.println(rootloader);
//测试当前类是由哪个加载器加载
ClassLoader c = Class.forName("Reflection.test06").getClassLoader();
System.out.println(c);
//测试JDK内置类由谁加载
ClassLoader c1 = Class.forName("java.lang.Integer").getClassLoader();
System.out.println(c1);
}
}