java类加载内存分析、初始化及运行时结构

java类加载内存分析

  • 方法区:包含了所有class和static变量
  • 堆:存放new的对象和数组
  • 栈:存放基本变量类型和引用对象的变量
  1. 加载:将class文件字节码内容加载到内存中,并将这些静态数据转化成方法区的运行时的数据结构,然后生成一个java.lang.Class对象

  2. 链接:将JVM类的二进制代码合并到JVM的运行状态之中的过程

    • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
    • 准备:正式为类变量(static)分配内存并设置变量默认初始值的阶段,这些内存都将在方法区中进行分配
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
  3. 初始化:

    • 执行类构造器()方法的过程:类构造器()方法是由编译器自动收集所有类变量的赋值动作和静态代码块中的语句的合并产生的(类构造器是构造类信息,不是构造该类对象的构造器 )
    • 当初始化一个类时,若发现其父类还没初始化,则先初始化其父类
    • 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步

package com.yuanyu.annandre;

public class Test06 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.m);
        /*
        1. 加载到内存,会产生一个Class对象
        2. 链接 ,链接结束后m=0
        3. 初始化
            <clinit>(){
                        System.out.println("A的静态代码块初始化");
                        m=300;
            }
            static int m=100;
         */
    }
}

class A{

    static {
        System.out.println("A的静态代码块初始化");
        m=300;
    }

    static int m=100;

    public A() {
        System.out.println("A的无参构造初始化");
    }
}

程序运行结果:

A的静态代码块初始化
A的无参构造初始化
100

何时会发生类的初始化

  • 类的主动引用(一定会发生类的初始化)
    • 当虚拟机启动,先初始化main方法所在的类
    • new一个类的对象
    • 调用类的静态成员(除final常量)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用
    • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化,如:当通过子类引用父类的静态变量不会导致子类初始化
    • 通过数组定义类引用,不会触发此类的初始化
    • 引用常量不会触发此类的初始化(常量在链接阶段就被放入常量池中了)
package com.yuanyu.annandre;

//测试类何时初始化
public class Test07 {
    static {
        System.out.println("Main方法被加载");
    }
    
    public static void main(String[] args) throws ClassNotFoundException {
        //主动引用
        //Son son = new Son();    //Main方法被加载
                                 //父类被加载
                                //子类被加载
        
         //通过反射主动引用
        //Class.forName("com.yuanyu.annandre.Son");  //Main方法被加载
                                                    //父类被加载
                                                   //子类被加载

        //不会产生类的初始化
        //System.out.println(Son.a);   //Main方法被加载
                                      //父类被加载
                                     //1
      
        //Son[] array = new Son[5]; //Main方法被加载

        //System.out.println(Son.b); //Main方法被加载
                                    //2
    }
}

class Father{
    static int a=1;
    static {
        System.out.println("父类被加载");
    }
}

class Son extends Father{
    static {
        System.out.println("子类被加载");
        m=300;
    }

    static int m=100;
    static final int b=2;
}

类缓存

标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到加载器中将维持加载(缓存)一段时间

不过JVM垃圾回收机制可以回收这些Class对象

类加载器

作用:把类装在进内存

  • 引导类加载器:负责Java平台核心库 ,由C++编写,JVM自带的加载器,用来装在核心类库,无法直接获取

    (rt.jar)

  • 扩展类加载器:将jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库

  • 系统类加载器:将java -classpath或-D java.class.path所指定目录下的jar包装入工作,是最常用的加载器

获取类加载器:

package com.yuanyu.annandre;

public class Test08 {
    public static void main(String[] args) {

        //获取类的系统加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获取系统类加载器的父类加载器-》扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        //获取扩展类加载器的父类加载器-》根加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
    }
}

程序运行结果:

package com.yuanyu.annandre;

public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取当前类的加载器
        ClassLoader classLoader = Class.forName("com.yuanyu.annandre.Test08").getClassLoader();
        System.out.println(classLoader);

        //测试JDK内置类的加载器
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);

        //获取系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
      
        /*
        D:\Java environment\jdk1.8\jre\lib\charsets.jar;
        D:\Java environment\jdk1.8\jre\lib\deploy.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\access-bridge-64.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\cldrdata.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\dnsns.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\jaccess.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\jfxrt.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\localedata.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\nashorn.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\sunec.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\sunjce_provider.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\sunmscapi.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\sunpkcs11.jar;
        D:\Java environment\jdk1.8\jre\lib\ext\zipfs.jar;
        D:\Java environment\jdk1.8\jre\lib\javaws.jar;
        D:\Java environment\jdk1.8\jre\lib\jce.jar;
        D:\Java environment\jdk1.8\jre\lib\jfr.jar;
        D:\Java environment\jdk1.8\jre\lib\jfxswt.jar;
        D:\Java environment\jdk1.8\jre\lib\jsse.jar;
        D:\Java environment\jdk1.8\jre\lib\management-agent.jar;
        D:\Java environment\jdk1.8\jre\lib\plugin.jar;
        D:\Java environment\jdk1.8\jre\lib\resources.jar;
        D:\Java environment\jdk1.8\jre\lib\rt.jar;
        F:\java\HELLO\out\production\HELLO;
        F:\java\HELLO\src\com\lib\commons-io-2.11.0.jar;
        D:\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar

         */
    }
}

获取类的运行时结构

package com.yuanyu.annandre;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

//获取类的信息
public class Test09 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.yuanyu.annandre.User");

        //获得类的名字
        System.out.println(c1.getName()); //获得包名+类名 com.yuanyu.annandre.User
        System.out.println(c1.getSimpleName()); //获得类名 User

        //获得类的属性
        System.out.println("====================================");
        Field[] fields = c1.getFields(); //只能找到public属性包含父类
        Field[] declaredFields = c1.getDeclaredFields(); //找到该类的全部属性
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        //获得类的指定属性
        System.out.println("====================================");
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        //获得指定方法
        System.out.println("====================================");
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        //获得构造器
        System.out.println("====================================");
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor); //获取所有public构造器包含父类
        }

        Constructor[] declaredConstructors = c1.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("#"+declaredConstructor); //获取本类所有构造器
        }

        //获得指定的构造器
        System.out.println("====================================");
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println(declaredConstructor);
    }
}

程序运行结果:

posted @   原语  阅读(227)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示