用Java实现JVM第二章《搜索class文件》

小傅哥
优质创作者: Java技术领域
领域专家: 后端开发技术领域
2019-05-06 01:10:07
案例简述
本章节主要了解Java虚拟机从哪里寻找class文件并且读取class内字节码

环境准备
1、jdk 1.8.0
2、IntelliJ IDEA Community Edition 2018.3.1 x64
3、Notepad++ (插件安装HEX-Editor,用于查看class字节)

配置信息
1、调试配置
2.1、配置位置:Run/Debug Configurations -> program arguments
2.2、配置内容:-Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-02\target\test-classes\org\itstack\demo\test\HelloWorld

代码示例


pom.xml
<!-- 命令行参数解析器 -->
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>1.72</version>
</dependency>


CompositeEntry.java
package org.itstack.demo.jvm.classpath.impl;

import org.itstack.demo.jvm.classpath.Entry;

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

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/24
*/
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(Entry.create(path));
}
}

@Override
public byte[] readClass(String className) throws IOException {
for (Entry entry : entryList) {
try {
return entry.readClass(className);
} catch (Exception ignored) {
//ignored
}
}
throw new IOException("class not found " + className);
}


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

}


DirEntry.java
package org.itstack.demo.jvm.classpath.impl;

import org.itstack.demo.jvm.classpath.Entry;

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

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/24
* 目录形式的类路径
*/
public class DirEntry implements Entry {

private Path absolutePath;

public DirEntry(String 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() {
return this.absolutePath.toString();
}
}


WildcardEntry.java
package org.itstack.demo.jvm.classpath.impl;

import org.itstack.demo.jvm.classpath.Entry;

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;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/24
* 通配符类路径,继承CompositeEntry
*/
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 "";
}
}

}


ZipEntry.java
package org.itstack.demo.jvm.classpath.impl;

import org.itstack.demo.jvm.classpath.Entry;

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

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/24
* zip/zar文件形式类路径
*/
public class ZipEntry implements Entry {

private Path absolutePath;

public ZipEntry(String path) {
//获取绝对路径
this.absolutePath = Paths.get(path).toAbsolutePath();
}

@Override
public byte[] readClass(String className) throws IOException {
try (FileSystem zipFs = FileSystems.newFileSystem(absolutePath, null)) {
return Files.readAllBytes(zipFs.getPath(className));
}
}

@Override
public String toString() {
return this.absolutePath.toString();
}

}


Classpath.java
package org.itstack.demo.jvm.classpath;

import org.itstack.demo.jvm.classpath.impl.WildcardEntry;

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

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/24
* 类路径
*/
public class Classpath {

private Entry bootstrapClasspath; //启动类路径
private Entry extensionClasspath; //扩展类路径
private Entry userClasspath; //用户类路径

public Classpath(String jreOption, String cpOption) {
//启动类&扩展类 "C:\Program Files\Java\jdk1.8.0_161\jre"
bootstrapAndExtensionClasspath(jreOption);
//用户类 E:\..\org\itstack\demo\test\HelloWorld
parseUserClasspath(cpOption);
}

private void bootstrapAndExtensionClasspath(String jreOption) {

String jreDir = getJreDir(jreOption);

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

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

}

private static String getJreDir(String jreOption) {
if (jreOption != null && Files.exists(Paths.get(jreOption))) {
return jreOption;
}
if (Files.exists(Paths.get("./jre"))) {
return "./jre";
}
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 (cpOption == null) {
cpOption = ".";
}
userClasspath = Entry.create(cpOption);
}

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

//[readClass]启动类路径
try {
return bootstrapClasspath.readClass(className);
} catch (Exception ignored) {
//ignored
}

//[readClass]扩展类路径
try {
return extensionClasspath.readClass(className);
} catch (Exception ignored) {
//ignored
}

//[readClass]用户类路径
return userClasspath.readClass(className);
}

}


Entry.java
package org.itstack.demo.jvm.classpath;

import org.itstack.demo.jvm.classpath.impl.CompositeEntry;
import org.itstack.demo.jvm.classpath.impl.DirEntry;
import org.itstack.demo.jvm.classpath.impl.WildcardEntry;
import org.itstack.demo.jvm.classpath.impl.ZipEntry;

import java.io.File;
import java.io.IOException;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/24
* 类路径接口
*/
public interface Entry {

byte[] readClass(String className) throws IOException;

static Entry create(String path) {

//File.pathSeparator;路径分隔符(win\linux)
if (path.contains(File.pathSeparator)) {
return new CompositeEntry(path);
}

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

if (path.endsWith(".jar") || path.endsWith(".JAR") ||
path.endsWith(".zip") || path.endsWith(".ZIP")) {
return new ZipEntry(path);
}

return new DirEntry(path);
}

}


Cmd.java
package org.itstack.demo.jvm;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;

import java.util.List;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/24
*/
public class Cmd {

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

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

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

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

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

boolean ok;

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

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

static Cmd parse(String[] argv) {
Cmd args = new Cmd();
JCommander cmd = JCommander.newBuilder().addObject(args).build();
cmd.parse(argv);
args.ok = true;
return args;
}

}


测试结果 验证Notepad++ 打开HelloWorld.class,在'插件'工具中选HEX-Editor
classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aa class:E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-02\target\test-classes\org\itstack\demo\test\HelloWorld args:null
classData:
ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
00 1b 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 22 4c 6f 72 67 2f 69 74
73 74 61 63 6b 2f 64 65 6d 6f 2f 74 65 73 74 2f ...



...全文
143 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
手把手视频详细讲解项目开发全过程,需要的小伙伴自行百度网盘下载,链接见附件,永久有效。 课程简介 JVMJava 程序的运行环境,学习 JVM,方能了解 Java 程序是如何被执行的,为进一步深入底层原理乃至程序性能调优打好基础。通过学习这门课程,你将掌握:1. JVM 内存结构的组成、各部分功能作用,学会利用内存诊断工具排查内存相关问题;2. JVM 的招牌功能-垃圾回收机制是如何工作的,如何进行垃圾回收调优;3. Java 程序从编译为字节码到加载到运行的全流程,各个阶段的优化处理;4. 了解 Java 内存模型相关知识,见识多线程并发读写共享数据时的问题和 Java 的解决方案。 适应人群 有一定的Java基础,希望提升 Java 内功的人群。 课程亮点 * 系统地学习 JVM 内存结构,垃圾回收、字节码与类加载技术。 * 在内存结构章节,能够学习掌握 JVM内存溢出现象,堆栈内存结构,利用内存诊断工具排查问题。彻底分析 StringTable的相关知识与性能优化,掌握直接内存分配原理和释放手段。 * 在垃圾回收章节,不仅会介绍垃圾回收算法、分代垃圾回收机制,还会重点介绍 G1 垃圾回收器,辨析 Full GC 发生条件,jdk8以来对垃圾回收的优化,以及垃圾回收的调优法则。 * 在字节码与类加载技术章节,会从一个 class 文件开始分析其每一字节的含义。学习字节码指令的的运行流程,字节码指令与常量池、方法区的关系。掌握条件分支、循环控制、异常处理、构造方法在字节码级别的实现原理,利用HSDB工具理解多态原理。还会涉及从编译期的语法糖处理,到类加载的各个阶段,直至运行期的各项优化的详细讲解。最后不要错过方法反射优化的底层分析。 * 最后的加餐环节是带着你理解 Java 内存模型:见识多线程读写共享数据的原子性、可见性、有序性,以及很多人解释不清楚的 happens-before 规则。当然还不能少了 CAS 和 synchronized 优化。 主讲内容 第一章:引言 1. 什么是 JVM ? 2. 学习 JVM 有什么用 ? 3. 常见的 JVM 4. 学习路线 第二章:内存结构 1. 程序计数器 2. 虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区 6. 直接内存 第三章:垃圾回收 1. 如何判断对象可以回收 2. 垃圾回收算法 3. 分代垃圾回收 4. 垃圾回收器 5. 垃圾回收调优 第四章:类加载与字节码技术 1. 类文件结构 2. 字节码指令 3. 编译期处理 4. 类加载阶段 5. 类加载器 6. 运行期优化 第五章:内存模型 1. Java 内存模型 2. 可见性 3. 有序性 4. CAS 与原子类 5. synchronized 优化

62,625

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧