main方法_main方法启动过程

main方法想必大家都很熟悉了,但是具体是怎么启动的, 不知道是否知道, 今天简单说下


  1. Demo
public class MyString {
    
    public static final int var  = 1;
    
    public static void main(String[] args) {
        System.out.println("aa");
    }
}

2. 下面就Step By Step说下

第一步:

main函数启动时, 首先会执行到LauncherHelper->checkAndLoadMain

public static Class<?> checkAndLoadMain(boolean var0, int var1, String var2) {
        initOutput(var0);
        String var3 = null;
        switch(var1) {
        case 1:
            var3 = var2;
            break;
        case 2:
            var3 = getMainClassFromJar(var2);
            break;
        default:
            throw new InternalError("" + var1 + ": Unknown launch mode");
        }

        var3 = var3.replace('/', '.');
        Class var4 = null;

        try {
            var4 = scloader.loadClass(var3);
        } catch (ClassNotFoundException | NoClassDefFoundError var8) {
            if(System.getProperty("os.name", "").contains("OS X") && Normalizer.isNormalized(var3, Form.NFD)) {
                try {
                    var4 = scloader.loadClass(Normalizer.normalize(var3, Form.NFC));
                } catch (ClassNotFoundException | NoClassDefFoundError var7) {
                    abort(var8, "java.launcher.cls.error1", new Object[]{var3});
                }
            } else {
                abort(var8, "java.launcher.cls.error1", new Object[]{var3});
            }
        }

        appClass = var4;
        if(!var4.equals(LauncherHelper.FXHelper.class) && !LauncherHelper.FXHelper.doesExtendFXApplication(var4)) {
            validateMainClass(var4);
            return var4;
        } else {
            LauncherHelper.FXHelper.setFXLaunchParameters(var2, var1);
            return LauncherHelper.FXHelper.class;
        }
    }

说明:

var1参数表示是传入的class还是jar, 如果是2, 需要从jar中读取class信息

特别注意 scloader.loadClass(var3);

但是这个scloader是什么呢? 我们调试下:

static {
        outBuf = new StringBuilder();
        trace = VM.getSavedProperty("sun.java.launcher.diag") != null;
        scloader = ClassLoader.getSystemClassLoader();   // 
        encoding = null;
        isCharsetSupported = false;
    }

559b1b0f480da33750d4a1f8ce97dfa8.png

第二步: 类加载

57113b2b8b43a6171bb546eab37ba626.png

super就进入了AppClassLoader方法:

076ea21c26e81e63c32891e53c9571fe.png

下面自然说下双亲委派机制了:

2c4b85c9585428cfb80710dff13a9d97.png

那我问下, 为啥不能直接从上往下加载呢? 还这么绕弯子呢?

其实, 我们加载的时候并不是这个类只new一次, 或者加载一次, 这样做的好处只是第一次加载慢了, 剩余时间是直接可以加载一步到位的, 缓存其中.

类加载器分类:

  • 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader
  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

说明:

不同的类加载器加载相同的类路径是不同的类

相同的类package路径是无法加载成功的, package名是不能重复的.否则报错: Prohibited package name:

那么类是怎么加载的呢?

一句话就是类加载器读取二进制class文件, 然后填充堆区, 方法区等的过程. 具体如下:

1197817e9432327b43316e60550c12d7.png

加载阶段

类的加载阶段是将class文件中的二进制数据读取到内存中,然后将该字节流所代表的静态存储结构转化为方法区中运行时的数据结构,并且在堆内存中生成一个该类的java.lang.class对象,作为方法区数据结构的入口。

类加载阶段的最终产物的堆内存中的class对象,对于同一个Classloader对象,不管某各类被加载多少次,对应堆内存中的class对象始终只有一个。

类加载阶段发生在连接阶段之前,但连接阶段不必等加载阶段结束才开始,可以交叉工作。

连接阶段

类的连接阶段包括三个小的过程:分别是验证、准备和解析。

(1)验证

验证在连接阶段中的主要目地是确保class文件的字节流所包含的内容符合JVM规范,并且不会出现危害JVM自身安全的代码。但验证不符合要求时,会抛出VerifyError这样的异常后其子异常。

主要验证内容有:

验证文件格式:包括文件头部的魔术因子、class文件主次版本号、class文件的MD5指纹、变量类型是否支持等

元数据的验证:对class的字节流进行语义分析,判断是否符合JVM规范。简单来说就是java语法的正确性

字符码验证:主要验证程序的控制流程,如循环、分支等

符号引用验证:验证符号引用转换为直接引用时的合法性,保证解析动作的顺利执行。比如不能访问引用类的私有方法、全限定名称是否能找到相关的类。

(2)准备

准备阶段主要做的事就是在方法区为静态变量发配内存以及赋初始默认值(区别于初始化阶段赋的程序指定的真实值)

注意:final修饰的静态常量在编译阶段就已经赋值,不会导致类的初始化,是一种被动引用,因此也不存在连接阶段。

(3)解析

解析就是在常量池中寻找类、接口、字段和方法的符号引用,并且将这些符号引用替换成直接引用的过程。

解析过程主要针对类接口、字段、类方法和接口方法四类进行。

初始化阶段
初始化阶段是类的加载过程的最后一个阶段,该阶段主要做一件事情就是执行< clinit>(),该方法会为所有的静态变量赋予正确的值。

0ce1fd35a6a94efae77e1cde1642e0f5.png
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值