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/…