(SUB)class文件详解

 class文件生命周期

 

3e081379db240e0634aeae55aaa1b164.png

5beb8e07fa1f4b9490d9dd2849d2d10e.png

前4字节魔数

minor version,major version:版本号

constant pool count:常量池,前两个字节常量池长度,

constant pool:后面跟的内容(内容实际长度为定义的长度-1(class预留了一个自己用))

access flags:类的访问权限

this class:2字节记录,指向常量池中类名的引用

superclass:指向父类类名引用

interface count:表示实现了多少接口,

interfaces:后面跟的与之相对应接口的常量池的引用(interfaces)

fields count:属性长度

fields:具体属性指向常量池位置(成员变量存在对应的methods中)

methods count:方法数量

methods:一个个的方法

attribute count:属性

attribute:各种属性,最重要的是code:方法里的代码

加载过程(双亲委派机制)

d63a25ac651c4d568281303568624419.png

loading:

将class加载内存

 父加载器不是类加载器的加载器,也不是类加载器的父类加载器!!! 

d80d73f28724452f9967ac3f5dca4d36.png

类加载器的父加载器关系(双亲委派模型)

695884c1412b494084013b879865a644.png

父加载器在构造方法中指定

代码:

System.out.println(String.class.getClassLoader());

System.out.println(sun.awt.HKSCS.class.getClassLoader());

System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());

System.out.println(T002_ClassLoaderLevel.class.getClassLoader());

System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader().getClass().getClassLoader());

System.out.println(T002_ClassLoaderLevel.class.getClassLoader().getClass().getClassLoader());

System.out.println(new T006_MSBClassLoader().getParent());

System.out.println(ClassLoader.getSystemClassLoader());

运行结果:

null

null

sun.misc.Launcher$ExtClassLoader@677327b6

sun.misc.Launcher$AppClassLoader@18b4aac2

null

null

sun.misc.Launcher$AppClassLoader@18b4aac2

sun.misc.Launcher$AppClassLoader@18b4aac2

结果第一行为null:表明用的类加载器为bootstrap,bootstrap类加载器c++实现,java中直接访问是没有类与之对应。因此代码中获取结果为null

第三行为extclassloader:因为该包在jdk的ext目录下

第四行的appclassloader:是自定义的类,类加载器为app

类加载器的加载器关系

bootstrap < —— extclassloader,appclassloader

类加载器的继承关系

be257a8f80234ae09a1fe99341f033e1.png

查看类加载器的scope

7b5198d520c64316b4531484ea4e8cd7.png

public class T003_ClassLoaderScope {
    public static void main(String[] args) {
        String pathBoot = System.getProperty("sun.boot.class.path");
        System.out.println(pathBoot.replaceAll(";", System.lineSeparator()));

        System.out.println("--------------------");
        String pathExt = System.getProperty("java.ext.dirs");
        System.out.println(pathExt.replaceAll(";", System.lineSeparator()));

        System.out.println("--------------------");
        String pathApp = System.getProperty("java.class.path");
        System.out.println(pathApp.replaceAll(";", System.lineSeparator()));
    }
}

双亲委派目的:为了安全!

思考:如果java.lang.string由自定义加载器加载,或自定义String类被appclassloader加载的风险。

举例:如果自定义了String类并打包给客户,客户代码中有获取密码的逻辑是将密码转成string,此时如果不是双亲委派模型,没有自上而下加载过程,那就会加载到app classloader中自定义的String,出现密码泄露风险

parent是如何指定的,如何打破双亲委派、何时打破过双亲委派

            1. 用super(parent)指定

2217c682de64419483df77768ad738bb.png
            2. 双亲委派的打破
               1. 如何打破:重写loadClass()
               2. 何时打破过?
                  1. JDK1.2之前,自定义ClassLoader都必须重写loadClass()
                  2. ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
                  3. 热启动,热部署
                     1. osgi tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本)

package com.mashibing.jvm.c2_classloader;

import com.mashibing.jvm.Hello;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class T012_ClassReloading2 {
    private static class MyLoader extends ClassLoader {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {

            File f = new File("C:/work/ijprojects/JVM/out/production/JVM/" + name.replace(".", "/").concat(".class"));

            if(!f.exists()) return super.loadClass(name);

            try {

                InputStream is = new FileInputStream(f);

                byte[] b = new byte[is.available()];
                is.read(b);
                return defineClass(name, b, 0, b.length);
            } catch (IOException e) {
                e.printStackTrace();
            }

            return super.loadClass(name);
        }
    }

    public static void main(String[] args) throws Exception {
        MyLoader m = new MyLoader();
        Class clazz = m.loadClass("com.mashibing.jvm.Hello");

        m = new MyLoader();
        Class clazzNew = m.loadClass("com.mashibing.jvm.Hello");

        System.out.println(clazz == clazzNew);
    }
}

自定义类加载器

         1. extends ClassLoader
         2. overwrite findClass() -> defineClass(byte[] -> Class clazz)
         3. 加密

9cb7ce5f7bf44a1f850e1fa6e08868ba.png

自定义类加载器_每天都要进步一点点的博客-优快云博客

1.手动加载类

public class T005_LoadClassByHand {
    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz = T005_LoadClassByHand.class.getClassLoader().loadClass("com.mashibing.jvm.c2_classloader.T002_ClassLoaderLevel");
        System.out.println(clazz.getName());

        //利用类加载器加载资源,参考坦克图片的加载
        //T005_LoadClassByHand.class.getClassLoader().getResourceAsStream("");
    }
}

2.什么时候需要自己加载类:

spring动态代理,spring自己创建代理类,并放到内存

热部署

源码:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        // 自下而上的查找cache,是否已经加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 自下而上的查找cache,是否已经加载
                    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
            }
            // 自上而下执行findclass去找这个类(自定义classloader只用重写实现这个方法),
            // 如果是自己负责的就返回,找不到返回null,下一级继续找
            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;
    }
}

// 设计模式:模板方法

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

ClassLoader的源码

         1. findInCache -> parent.loadClass -> findClass()

lazyloading

规定什么时候必须初始化class

            –new getstatic putstatic invokestatic指令,访问final变量除外

            –java.lang.reflect对类进行反射调用时

            –初始化子类的时候,父类首先初始化

            –虚拟机启动时,被执行的主类必须初始化

            –动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic的方法句柄时,该类必须初始化

2d33c48a8b5b4adab124441962349e1a.png

 

verification:

校验,验证文件是否符合JVM规定

preparation:

将class文件静态变量赋值默认值(如int i=8,此处赋值为0)

resolution:

1. 将类、方法、属性等符号引用解析为直接引用
2. 常量池中的各种符号引用(指向class中常量池的位置)解析为指针、偏移量等能访问到的内存地址的直接引用

initializing:

调用类初始化代码 <clinit>,给静态成员变量赋初始值(此处将 i 赋值8)

类加载器 本质

是个class

加载class过程做了两件事 

1. 二进制内容被放到内存 

2. 生成了一个class对象指向了二进制内容

关于编译器

默认模式是混合模式,就是混合使用解释器加热点代码编译。什么叫热点代码编译?
多次被调用的方法,多次被调用的循环进行编译,怎么检测呢?就是用一个计数器,每个方法上都有一个方法,计数器循环有循环计数器。结果在发现某个方法一秒钟执行了超过某个10万次。我要对他进行编译,拿大腿想一想我该怎么办?直接编译成本地代码,再用的话直接用本地的。不用解释器执行了。

这时候会有人问,为什么不直接都编译成本地代码呢,执行效率更高,因为Java解释器现在效率已经非常高了,在一些简单的代码上它不属于便器。第二点,如果你有一段代码执行文件,特别特别多各种各样的类库,有时候好几十个class,这是正常的。你上来二话不说先给编译器让他编译,编译的过程会长的吓人,所以现在默认的模式是混合模式,但是完全可以用参数来指定到底是什么模式。

6e3b31248a794c269925be69baf9f68a.png

 编译比解释启动慢,执行快

检测热点代码:-XX:CompileThreshold = 10000

 

### 回答1: 在 TURBO C 2.0 中,您可以使用 biosdisk() 函数来格式化软盘。 下面是一段代码示例: ``` #include <stdio.h> #include <dos.h> int main() { // 定义扇区的大小 unsigned int sector_size[3] = {512, 512, 1024}; // 定义填充字节 unsigned char fill_char[3] = {0xF6, 0xF6, 0x48}; // 格式化磁道 70 面 0 的前三个扇区 for (int i = 0; i < 3; i++) { int result = biosdisk(2, 0, 70, 0, i + 1, 1, sector_size[i], fill_char[i]); if (result != 0) { printf("格式化扇区 %d 失败,返回码: %d\n", i + 1, result); } } return 0; } ``` 请注意,在使用 biosdisk() 函数之前,需要包含 dos.h 头文件。 希望这能帮到您! ### 回答2: 使用TURBO C 2.0语言,可以通过以下代码格式化软盘的70磁道0面,并分别格式化3个扇区,大小分别为512字节、512字节和1024字节。其中,前两个扇区使用F6填充,第三个扇区使用48填充。 ```c #include<stdlib.h> #include<stdio.h> #include<dos.h> void formatFloppyDisk(){ union REGS regs; regs.h.ah = 0x0;// To format a floppy disk, we set AH=0 regs.h.dl = 0;// Drive number (0=A, 1=B, etc.) regs.x.cx = 0;// Track number to format regs.h.dh = 0;// Head number regs.h.al = 0;// Sector size (0=default, 1=512 bytes, 2=1024 bytes, 3=2048 bytes etc.) int FILL_BYTE = 0;// The byte value to fill the sectors with during formatting int NUM_SECTORS = 3;// Number of sectors to format // To format 70th track 0th head regs.x.ax = 0x1301; // 0x13 = Reset disk system, 01H = Reset only specified drive int86(0x13, &regs, &regs); // BIOS interrupt to reset disk system for (int i=0; i<NUM_SECTORS; i++){ regs.x.ax = 0x3101; // 0x31 = Write Format, 01H = Format only current track regs.x.bx = 0x0001; // 0x00 = Drive A:, 01H = Head 1, 0 = Generate ID Field depending on the disk in the drive 1 = Keep the ID Field all zeros regs.x.cx = 0x0170; // Track number=70(0-79 range) regs.h.dh = 0x00; // Head number=0 or 1 regs.h.al = 0x02; // Control byte=always zero regs.x.dx = i+1; // Sector number starting from 1 regs.x.si = 0x0000; // segment and offset of read/write buffer regs.x.di = 0x0000; // segment and offset of result if(i == 2){ FILL_BYTE = 0x48; // Fill the third sector with 48 regs.x.ax = 0x3102; // 0x31 = Write Format, 02H = Format sequential tracks immediately following the one being formatted }else{ FILL_BYTE = 0xF6; // Fill the first two sectors with F6 } regs.h.ah = FILL_BYTE; // Fill the sector with specified byte int86(0x13, &regs, &regs); // BIOS interrupt to format the specified sector } } int main(){ formatFloppyDisk(); return 0; } ``` 上述代码使用了INT 0x13,即BIOS中断服务例程,来执行软盘格式化操作。通过设置寄存器的不同参数,可以指定要格式化的磁道、面、扇区大小和填充字节。在这个例子中,我们格式化了软盘70磁道0面的3个扇区,前两个扇区使用F6填充,第三个扇区使用48填充。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值