IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> JVM基础 -> 正文阅读

[Java知识库]JVM基础

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对象时进行初始化

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

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-23 12:20:41  更:2021-10-23 12:22:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:01:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码