JVM基础

本文详细介绍了Java从编码到执行的过程,包括JVM的基础知识,如Class文件的解读、结构和加载机制。讲解了类加载器的双亲委派模型,以及类加载的具体步骤。同时,通过实例探讨了类初始化时静态变量的赋值行为。
摘要由CSDN通过智能技术生成

Learn JVM

1. JVM 基础

1.1 Java从编码到执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQguryO5-1634915025194)(https://files.mdnice.com/user/20547/c67ccb8e-5c16-414a-a11b-7ca574cf896a.png)]

Java-跨平台的语言:一处编译,可以在运行JVM的任何平台运行

JVM-跨语言的平台:无论什么语言,只要按照JVM的规范编译成符合Class文件的规范的文件,既可以在JVM上运行

1.2 JAVA&JVM文档

查看Java语言和JVM规范,可以查看官方的文档,访问官网查看需要的版本即可

Java SE Specifications (oracle.com)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XA0zwdSZ-1634915025197)(D:\OneDrive\桌面备份\typora-user-images\1633328816693.png)]

1.3常见的JVM的实现

我们使用的JVM可以控制台查看:

2. CLASS文件解读

2.1 如何将java源码生成.class文件

使用命令行,我们可以使用javac指令将编辑好的java文件编译成.class文件,当然也可以使用IDE工具进行编译生成.class文件

但是我们使用IDE工具打开的.class文件,工具会帮我们反编译,所以要查看原格式文件需要借助插件,这里我们使用BinED

装好之后右击.class文件,选择 Open As Binary即可以16进制,二进制,或者八进制,十进制查看

下面我们以16进制查看.class文件然后分析,下图是我们上面简单的代码编译后生成的.class文件的字节码code

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmSShiSp-1634915025202)(D:\OneDrive\桌面备份\typora-user-images\1633355532799.png)]

后面为了方便我们更好的阅读class文件,选择另一个IDEA的插件:jclasslib

鼠标放在要查看class源码文件内部,在工具栏View中选择Show Bytecode With Jclasslib,打开之后如下图所示,方便查看里面的内容

2.2 CLASS文件的格式

这里实验使用的是JDK1.8版本,查看JAVA官网提供的手册

https://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf

第四章可以看到其中对于ClassFile结构的定义,源文件描述如下:

注: u4 表示4个字节,对应到.class文件可以确认前面的4个字节"CA FE BA BE"是这所谓的magic

2.2.1 magic

0x CAFE BABE

然后再往下看这个class文件

2.2.2 minor_version&major_version

minor_version: 0x0000

major_version: 0x0034

对应的10进制的值为52,然后我们查看文档中major versions format 可以看出对应的为java 8

2.2.3 constant_pool_count

0x0010 = 16

常量池数量

2.2.4 constant_pool[]

2.2.5 常量池的类型

下面开始分析class文件常量池部分

0x0A 对应十进制的10,查表可以看出tag 10为Constant_methodref

3. Class加载器ClassLoader

一个类,编译后生成class文件在硬盘上,需要先将class文件加载到内存,加载过程包括以下几步:

1. Loading

2. Linking:

​		Verification

​		Preparation,class静态变量赋值默认值

​		Resolution,静态变量赋值,初始化

这里先来看Loading过程

3.1 类加载器

3.1.1 双亲委派加载机制

所谓的父加载器,并不是加载器的父类加载器,也不是类加载器的加载器,只是类加载时的关系

为什么要有双亲委派这种类加载机制?主要是为了安全(核心类库中的类,防止自定义然后加载),以及效率(已经加载过的类不被重复加载),

所谓的双亲委派:

加载一个类时,先去查看是否已经加载,如果没有加载过,则让父加载器查看是否已加载,逐级向上,到顶级父加载器时也都没加载过则从上向下逐级尝试进行加载,知道加载完成,如果没有最终没有完成加载抛出异常

类加载的源代码如下:

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
3.1.2 类加载器的范围
3.1.3 自定义类加载器

继承ClassLoader类,重写loadClass(String name) 方法

示例:

package cn.qiyuandata.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

/**
 * Title:MyClassLoader01
 * <p>
 * Desc:
 * <p>
 * At 2021/10/10
 *
 * @author ZhaoJu
 */
public class MyClassLoader01 extends ClassLoader{

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        File f = new File("G:/document/code/Java/LearnJvm/out/production/LearnJvm/", name.replace(".", "/").concat(".class"));
        try {
            FileInputStream fis = new FileInputStream(f);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int b = 0;

            while ((b=fis.read()) !=0) {
                byteArrayOutputStream.write(b);
            }

            byte[] bytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.close();
            fis.close();//可以写的更加严谨

            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //throws ClassNotFoundException
        return super.findClass(name);
    }

}

使用举例:

热部署,一个类完成编辑编译后需要自动把新的class加载进内存实现替换,需要定义加载器

4. 编译

默认为混合模式,可以在启动参数设置编译模式,可以选择解释模式,或纯编译模式,对应如下

5. 初始化

静态变量赋默认值

静态变量赋初始值

看下面一段代码,Test类里有静态变量a,初始值为2,那下面一段代码的

package cn.qiyuandata.jvm;

public class InitTest01 {
    public static void main(String[] args) {
        System.out.println(Test.a);
    }
}

class Test{

    public static Test test = new Test();
    public static int a =2;
    private Test(){
        a++;
    }
}

这段代码输出的结果是几?答案是2

为什么呢,因为Test这个类在被调用时,先加载进来,然后声明test变量内存,这时test为null,a为默认值0

接下来调用构造方法a++,得到a=1

然后在执行a赋初始值a=2

所以最终结果为a=2

那么再来看下面一段代码呢,a与test的声明换了下位置,结果是几呢?

package cn.qiyuandata.jvm;

public class InitTest01 {
    public static void main(String[] args) {
        System.out.println(Test.a);
    }
}

class Test{

    public static int a =2;
    public static Test test = new Test();
    private Test(){
        a++;
    }
}

结果是3,为什么呢?

因为加载class后a赋默认值0,然后初始化为2

然后test声明为null

再然后执行构造方法,a++,值变成了3

以上是关于静态变量,接下来在讨论下关于成员变量的初始化

成员变量在new对象时进行初始化

初始化也是分为两步,先分配空间,声明变量,赋默认值,然后再赋初始值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值