This is the fifth day of my participation in the August More text Challenge. For details, see:August is more challenging

The paper

Which path to load class files to binary files?

Class loader

To start the class loader: The Bootstrap ClassLoader is responsible for loading classes that can be identified by the VIRTUAL machine (e.g. Rt.jar) and stored in the JDK, jre, lib, or in the -xbootclasspath directory. All classes starting with Java.* are loaded by the Bootstrap ClassLoader. The bootstrap classloader is not directly referenced by Java programs.

Extending the class loader: Extension ClassLoader, which is implemented by sun.misc.launcher $ExtClassLoader, is responsible for loading the JDK, jre, lib, ext directory, Or all libraries in the path specified by the java.ext.dirs system variable (such as classes beginning with Javax.*), developers can use the extended class loader directly.

Application class loaders: Application ClassLoader is implemented by sun.misc.Launcher$AppClassLoader. It is responsible for loading the class specified by the user ClassPath. If the application does not have its own class loader, this will generally be the default class loader for the application.

Parent delegation mechanism

1. When the AppClassLoader loads a class, it does not first attempt to load the class itself. Instead, it delegates the classloading request to the parent classloader, ExtClassLoader.

2. When an ExtClassLoader loads a class, it does not first attempt to load the class itself. Instead, it delegates the class loading request to the BootStrapClassLoader.

3. If the BootStrapClassLoader fails to load the class (for example, it does not find the class in $JAVA_HOME/jre/lib), the ExtClassLoader tries to load the class.

4. If the ExtClassLoader fails to load the ExtClassLoader, the AppClassLoader is used to load the ExtClassLoader. If the AppClassLoader also fails to load the ExtClassLoader, the ClassNotFoundException exception is reported.

advantage

System classes prevent multiple copies of the same bytecode from appearing in memory

Ensure the safe and stable operation of Java programs

Code sample

Entry interface

import java.io.IOException;

/ * * *@Author blackcat
 * @create2021/8/11 in *@version: 1.0
 * @description: Interface for reading classes */
public interface Entry {
    // The relative path to the class file. For example, to read the Java.lang. Object class, the argument passed would be Java /lang/ object.class
    byte[] readClass(String className) throws IOException;
}
Copy the code

DirEntry

import com.black.cat.jvm.classpath.Entry;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/ * * *@Author blackcat
 * @create 2021/8/11 14:46
 * @version: 1.0
 * @description: Absolute path (directory) */
public class DirEntry implements Entry {

    private Path absolutePath;

    public DirEntry(String path){
        // Get the absolute path
        this.absolutePath = Paths.get(path).toAbsolutePath();
    }

    @Override
    public byte[] readClass(String className) throws IOException {
        return Files.readAllBytes(absolutePath.resolve(className));
    }

    @Override
    public String toString(a) {
        return this.absolutePath.toString(); }}Copy the code

ZipEntry

import com.black.cat.jvm.classpath.Entry;

import java.io.IOException;
import java.nio.file.*;

/ * * *@Author blackcat
 * @create2021/8/11 any valiant man *@version: 1.0
 * @description: Classpath */ in zip/zar or JAR files
public class ZipEntry implements Entry {

    private Path absolutePath;

    public ZipEntry(String path) {
        // Get the absolute path
        this.absolutePath = Paths.get(path).toAbsolutePath();
    }

    @Override
    public byte[] readClass(String className) throws IOException {
        try (FileSystem zipFs = FileSystems.newFileSystem(absolutePath, null)) {
            returnFiles.readAllBytes(zipFs.getPath(className)); }}@Override
    public String toString(a) {
        return this.absolutePath.toString(); }}Copy the code

CompositeEntry

import com.black.cat.jvm.classpath.Entry;
import com.black.cat.jvm.classpath.EntryFactory;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/ * * *@Author blackcat
 * @create2021/8/11 hold *@version: 1.0
 * @description: * /
@Slf4j
public class CompositeEntry implements Entry {

    private final List<Entry> entryList = new ArrayList<>();


    public CompositeEntry(String pathList) {
        String[] paths = pathList.split(File.pathSeparator);
        for(String path : paths) { entryList.add(EntryFactory.create(path)); }}@Override
    public byte[] readClass(String className) throws IOException {
        for (Entry entry : entryList) {
            try {
                return entry.readClass(className);
            } catch (Exception ignored) {
                //ignoredlog.info(entry.toString()); }}throw new IOException("class not found " + className);
    }

    @Override
    public String toString(a) {
        String[] strs = new String[entryList.size()];
        for (int i = 0; i < entryList.size(); i++) {
            strs[i] = entryList.get(i).toString();
        }
        returnString.join(File.pathSeparator, strs); }}Copy the code

WildcardEntry

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;

/ * * *@Author blackcat
 * @create 2021/8/11 14:55
 * @version: 1.0
 * @description: Indicates the wildcard character */
public class WildcardEntry extends CompositeEntry {

    public WildcardEntry(String path) {
        super(toPathList(path));
    }

    private static String toPathList(String wildcardPath) {
        String baseDir = wildcardPath.replace("*".""); // remove *
        try {
            return Files.walk(Paths.get(baseDir))
                    .filter(Files::isRegularFile)
                    .map(Path::toString)
                    .filter(p -> p.endsWith(".jar") || p.endsWith(".JAR"))
                    .collect(Collectors.joining(File.pathSeparator));
        } catch (IOException e) {
            return ""; }}}Copy the code

EntryFactory

import com.black.cat.jvm.classpath.impl.CompositeEntry;
import com.black.cat.jvm.classpath.impl.DirEntry;
import com.black.cat.jvm.classpath.impl.WildcardEntry;
import com.black.cat.jvm.classpath.impl.ZipEntry;

import java.io.File;

/ * * *@Author blackcat
 * @create2021/8/11 land *@version: 1.0
 * @description: Entry * / factory
public class EntryFactory {

    public static Entry create(String path) {
         ':' '; '
        if (path.contains(File.pathSeparator)) {
            return new CompositeEntry(path);
        }

        / / the wildcard
        if (path.endsWith("*")) {
            return new WildcardEntry(path);
        }

        / / the jar or zip
        if (path.endsWith(".jar") || path.endsWith(".JAR") ||
                path.endsWith(".zip") || path.endsWith(".ZIP")) {
            return new ZipEntry(path);
        }
        // Directory format
        return newDirEntry(path); }}Copy the code

Classpath

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

/ * * *@Author blackcat
 * @create2021/8/11 in *@version: 1.0
 * @description: Classpath load */
@Slf4j
public class Classpath {

    private Entry bootstrapClasspath;  // Start the classpath

    private Entry extensionClasspath;  // Extend the classpath

    private Entry userClasspath;       // User classpath

    public Classpath(String jreOption, String cpOption) {
        // Start class & extend class
        bootstrapAndExtensionClasspath(jreOption);
        / / user classes
        parseUserClasspath(cpOption);
    }

    private void bootstrapAndExtensionClasspath(String jreOption) {
        // Obtain the JRE directory
        String jreDir = getJreDir(jreOption);

        / /.. jre/lib/*
        String jreLibPath = Paths.get(jreDir, "lib") + File.separator + "*";
        bootstrapClasspath = EntryFactory.create(jreLibPath);

        / /.. jre/lib/ext/*
        String jreExtPath = Paths.get(jreDir, "lib"."ext") + File.separator + "*";
        extensionClasspath = EntryFactory.create(jreExtPath);

    }

    private static String getJreDir(String jreOption) {
        // Use the user-xjre parameter preferentially
        if(jreOption ! =null && Files.exists(Paths.get(jreOption))) {
            return jreOption;
        }
        // If the -xjre parameter is not entered, look for the JRE in the current directory
        if (Files.exists(Paths.get("./jre"))) {
            return "./jre";
        }
        // If you can't find it, use the JAVA_HOME environment variable
        String jh = System.getenv("JAVA_HOME");
        if(jh ! =null) {
            return Paths.get(jh, "jre").toString();
        }
        throw new RuntimeException("Can not find JRE folder!");
    }


    private void parseUserClasspath(String cpOption) {
        // If "-cp" is not configured, "-classpath" uses the current directory as the user directory path
        if (cpOption == null) {
            cpOption = ".";
        }
        userClasspath = EntryFactory.create(cpOption);
    }

    public byte[] readClass(String className) throws Exception {
        className = className + ".class";

        //[readClass] Starts the classpath
        try {
            return bootstrapClasspath.readClass(className);
        } catch (Exception ignored) {
            //ignored
            log.info("bootstrapClasspath ignore");
        }

        //[readClass] Extends the classpath
        try {
            return extensionClasspath.readClass(className);
        } catch (Exception ignored) {
            //ignored
            log.info("extensionClasspath ignore");
        }

        //[readClass] User classpath
        returnuserClasspath.readClass(className); }}Copy the code

test

/ * * *@Author blackcat
 * @create 2021/8/11 13:42
 * @version: 1.0
 * @description: Command line parameters */
public class Cmd {

    @Parameter(names = {"-?" , "-help"}, description = "print help message", help = true)
    boolean helpFlag = false;

    @Parameter(names = "-version", description = "print version and exit")
    boolean versionFlag = false;

    @Parameter(names = {"-cp", "-classpath"}, description = "classpath")
    String classpath;

    @Parameter(names = "-Xjre", description = "path to jre")
    String jre;

    @Parameter(description = "main class and args")
    List<String> mainClassAndArgs;

    boolean ok;

    String getMainClass(a) {
        returnmainClassAndArgs ! =null && !mainClassAndArgs.isEmpty()
                ? mainClassAndArgs.get(0)
                : null;
    }

    List<String> getAppArgs(a) {
        returnmainClassAndArgs ! =null && mainClassAndArgs.size() > 1
                ? mainClassAndArgs.subList(1, mainClassAndArgs.size())
                : null;
    }

    static Cmd parse(String[] argv) {
        Cmd cmd = new Cmd();
        try {
            JCommander.newBuilder().addObject(cmd).args(argv).build();
            cmd.ok = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        returncmd; * * *}}@Author blackcat
 * @create 2021/8/11 13:43
 * @version: 1.0
 * @description: Command line tool */public class Main {

    public static void main(String[] args) {
        String[] argv = {"-classpath"."D:\\develop\\code\\jjvm\\jvm-02\\target\\classes"."com.black.cat.jvm.MainTest"};
        Cmd cmd = Cmd.parse(argv);
        if(! cmd.ok || cmd.helpFlag) { System.out.println("Usage: 
      
[-options] class [args...] "
); return; } if (cmd.versionFlag) { System.out.println("Java version \" 1.8.0 comes with \ ""); return; } startJVM(cmd); } private static void startJVM(Cmd cmd) { System.out.printf("classpath:%s class:%s args:%s\n", cmd.classpath, cmd.getMainClass(), cmd.getAppArgs()); Classpath classpath = new Classpath(null, cmd.classpath); try { //java.lang.Object String className = cmd.getMainClass().replace("."."/"); byte[] classData = classpath.readClass(className); System.out.println("classData:"); for (byte b : classData) { // Hexadecimal output System.out.print(String.format("%02x", b & 0xff) + ""); } System.out.println(); } catch (Exception e) { System.out.println("Could not find or load main class "); e.printStackTrace(); }}}public class MainTest { public static void main(String[] args) throws IOException { System.out.println("Hello World"); }}Copy the code

The results of

ClassData:  ca fe ba be 00 00 00 34 00 25 0a 00 06 00 16 09 00 17 00 18 08 00 19 0a 00 1a 00 1b 07 00 1c 07 00 1d 01 00 06 3c 69 6e  69 74 3e 01 00 03 28 29 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63  61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01 00 04 74 68 69 73 01 00 1c 4c 63 6f 6d 2f 62 6c 61 63 6b 2f 63 61 74 2f  6a 76 6d 2f 4d 61 69 6e 54 65 73 74 3b 01 00 04 6d 61 69 6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69  6e 67 3b 29 56 01 00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 45 78 63  65 70 74 69 6f 6e 73 07 00 1e 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 0d 4d 61 69 6e 54 65 73 74 2e 6a 61 76 61 0c  00 07 00 08 07 00 1f 0c 00 20 00 21 01 00 0b 48 65 6c 6c 6f 20 57 6f 72 6c 64 07 00 22 0c 00 23 00 24 01 00 1a 63 6f 6d  2f 62 6c 61 63 6b 2f 63 61 74 2f 6a 76 6d 2f 4d 61 69 6e 54 65 73 74 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65  63 74 01 00 13 6a 61 76 61 2f 69 6f 2f 49 4f 45 78 63 65 70 74 69 6f 6e 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73  74 65 6d 01 00 03 6f 75 74 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76 61  2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 01 00 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f  53 74 72 69 6e 67 3b 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00 09 00 00 00 2f 00 01 00 01 00  00 00 05 2a b7 00 01 b1 00 00 00 02 00 0a 00 00 00 06 00 01 00 00 00 0b 00 0b 00 00 00 0c 00 01 00 00 00 05 00 0c 00 0d  00 00 00 09 00 0e 00 0f 00 02 00 09 00 00 00 37 00 02 00 01 00 00 00 09 b2 00 02 12 03 b6 00 04 b1 00 00 00 02 00 0a 00  00 00 0a 00 02 00 00 00 17 00 08 00 1f 00 0b 00 00 00 0c 00 01 00 00 00 09 00 10 00 11 00 00 00 12 00 00 00 04 00 01 00 13 00 01 00 14 00 00 00 02 00 15Copy the code

Gitee address

Gitee.com/feicc/jjvm/…