java逻辑第九章_深入理解jvm-(第九章)类加载及执行子系统的案例与实战
转载自:http://blog.csdn.net/coslay/article/details/49564789
概述
在Class文件格式与执行引擎这部分中,用户的程序能直接影响的内容并不太多,
Class文件以何种格式存储,类型何时加载、如何连接,以及虚拟机如何执行字节码指令等都是由虚拟机直接控制的行为,用户程序无法对其进行改变。能通过程序进行操作的,主要是字节码生成与类加载器这两部分的功能,但仅仅在如何处理这两点上,就已经出现了许多值得欣赏和借鉴的思路,这些思路后来成为了许多常用功能和程序实现的基础。
案例分析
Tomcat:正统的类加载器架构
主流的Java Web服务器
,如Tomcat、Jetty、WebLogic、WebSphere或其他笔者没有列举的服务器,都实现了自己定义的类加载器(一般都不止一个)。因为一个功能健全的Web服务器
,要解决如下几个问题:
部署在同一个服务器上的两个Web应用程序所使用的Java类库可以实现相互隔离。这是最基本的需求,两个不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类库可以互相独立使用。
部署在同一个服务器上的两个Web应用程序所使用的Java类库可以互相共享。这个需求也很常见,例如,用户可能有10个使用Spring组织的应用程序部署在同一台服务器上,如果把10份Spring分别存放在各个应用程序的隔离目录中,将会是很大的资源浪费——这主要倒不是浪费磁盘空间的问题,而是指类库在使用时都要被加载到服务器内存,如果类库不能共享
,虚拟机的方法区就会很容易出现过度膨胀的风险。
服务器需要尽可能地保证自身的安全不受部署的Web应用程序影响。目前,有许多主流的Java
Web服务器自身也是使用Java语言来实现的。因此
,服务器本身也有类库依赖的问题,一般来说,基于安全考虑,服务器所使用的类库应该与应用程序的类库互相独立。
支持JSP应用的Web服务器 ,大多数都需要支持HotSwap功能。我们知道,JSP文件最终要编译成Java
Class才能由虚拟机执行,但JSP文件由于其纯文本存储的特性,运行时修改的概率远远大于第三方类库或程序自身的Class文件。而且ASP、PHP和JSP这些网页应用也把修改后无须重启作为一个很大的“优势”来看待,因此“主流”的Web服务器都会支持JSP生成类的热替换,当然也有“非主流”的
,如运行在生产模式( Production Mode ) 下的WebLogic服务器默认就不会处理JSP文件的变化。
由于存在上述问题,在部署Web应用时
,单独的一个ClassPath就无法满足需求了,所以各种Web服务器都“不约而同”地提供了好几个ClassPath路径供用户存放第三方类库,这些路径一般都以“lib”或“classes”命名。被放置到不同路径中的类库,具备不同的访问范围和服务对象,通常,每一个目录都会有一个相应的自定义类加载器去加载放置在里面的Java类库。
现在 ,笔者就以Tomcat服务器为例,看一看Tomcat具体是如何规划用户类库结构和类加载器的。
在Tomcat目录结构中,有3组目录(“/common public class HotSwapClassLoader extends ClassLoader { public HotSwapClassLoader()
{ super(HotSwapClassLoader.class.getClassLoader()); }
public Class loadByte(byte[]
classByte) { return defineClass(null,
classByte, 0, classByte.length); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HotSwapClassLoader所做的事情仅仅是公开父类(即
中的protected方法defineClass()
,我们将会使用这个方法把提交执行的
或findClass() 方法 ,因此如果不算外部手工调用loadByte()
方法的话,这个类加载器的类查找范围与它的父类加载器是完全一致的,在被虚拟机调用时,它会按照双亲委派模型交给父类加载。构造函数中指定为加载HotSwapClassLoader类的类加载器也为父类加载器,这一步是实现提交的执行代码可以访问服务端引用类库的关键,下面我们来看看代码清单9-3。
第二个类是实现将java.lang.System替换为我们自己定义的HackSystem类的过程,它直接修改符合Class文件格式的byte[]数组中的常量池部分,将常量池中指定内容的
CONSTANT_UtfB_info常量替换为新的字符串,具体代码如代码清单9-4所示。
ClassModifier中涉及对byte[]数组操作的部分,主要是将byte[]与int和String互相转换,以及把对byte[]数据的替换操作封装在代码清单9-5所示的ByteUtils中。
代码清单9-4
ClassModifier的实现
public class ClassModifier {
private static final int CONSTANT_POOL_COUNT_INDEX = 8;
private static final int CONSTANT_Utf8_info = 1;
private static final int[] CONSTANT_ITEM_LENGTH = { -1, -1, -1, 5, 5, 9, 9, 3, 3, 5, 5, 5, 5 };
private static final int u1 = 1;
private static final int u2 = 2;
private byte[] classByte;
public ClassModifier(byte[] classByte) {
this.classByte = classByte;
}
public byte[] modifyUTF8Constant(String oldStr, String newStr) {
int cpc = getConstantPoolCount();
int offset = CONSTANT_POOL_COUNT_INDEX + u2;
for (int i = 0; i < cpc; i++) {
int tag = ByteUtils.bytes2Int(classByte, offset, u1);
if (tag == CONSTANT_Utf8_info) {
int len = ByteUtils.bytes2Int(classByte, offset + u1, u2);
offset += (u1 + u2);
String str = ByteUtils.bytes2String(classByte, offset, len);
if (str.equalsIgnoreCase(oldStr)) {
byte[] strBytes = ByteUtils.string2Bytes(newStr);
byte[] strLen = ByteUtils.int2Bytes(newStr.length(), u2);
classByte = ByteUtils.bytesReplace(classByte, offset - u2, u2, strLen);
classByte = ByteUtils.bytesReplace(classByte, offset, len, strBytes);
return classByte;
} else {
offset += len;
}
} else {
offset += CONSTANT_ITEM_LENGTH[tag];
}
}
return classByte;
}
public int getConstantPoolCount() {
return ByteUtils.bytes2Int(classByte, CONSTANT_POOL_COUNT_INDEX, u2);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
代码清单9-5
ByteUtils的实现
public class ByteUtils {
public static int bytes2Int(byte[] b, int start, int len) {
int sum = 0;
int end = start + len;
for (int i = start; i < end; i++) {
int n = ((int) b[i]) & 0xff;
n <<= (--len) * 8;
sum = n + sum;
}
return sum;
}
public static byte[] int2Bytes(int value, int len) {
byte[] b = new byte[len];
for (int i = 0; i < len; i++) {
b[len - i - 1] = (byte) ((value >> 8 * i) & 0xff);
}
return b;
}
public static String bytes2String(byte[] b, int start, int len) {
return new String(b, start, len);
}
public static byte[] string2Bytes(String str) {
return str.getBytes();
}
public static byte[] bytesReplace(byte[] originalBytes, int offset, int len, byte[] replaceBytes) {
byte[] newBytes = new byte[originalBytes.length + (replaceBytes.length - len)];
System.arraycopy(originalBytes, 0, newBytes, 0, offset);
System.arraycopy(replaceBytes, 0, newBytes, offset, replaceBytes.length);
System.arraycopy(originalBytes, offset + len, newBytes, offset + replaceBytes.length, originalBytes.length - offset - len);
return newBytes;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
经过ClassModifier处理后的byte[]数组才会传给HotSwapClassLoader.loadByte()方法进行类加载,byte[]数组在这里替换符号引用之后,与客户端直接在
,又避免了服务端修改标准输出后影响到其他程序的 输出。下面我们来看看代码清单9-4和代码清单9-5。
public class HackSystem {
public final static InputStream in = System.in;
private static ByteArrayOutputStream buffer = new ByteArrayOutputStream();
public final static PrintStream out = new PrintStream(buffer);
public final static PrintStream err = out;
public static String getBufferString() {
return buffer.toString();
}
public static void clearBuffer() {
buffer.reset();
}
public static void setSecurityManager(final SecurityManager s) {
System.setSecurityManager(s);
}
public static SecurityManager getSecurityManager() {
return System.getSecurityManager();
}
public static long currentTimeMillis() {
return System.currentTimeMillis();
}
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) {
System.arraycopy(src, srcPos, dest, destPos, length);
}
public static int identityHashCode(Object x) {
return System.identityHashCode(x);
}
// 下面所有的方法都与java.lang.System的名称一样
// 实现都是字节转调System的对应方法
// 因版面原因,省略了其他方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
至此,
4个支持类已经讲解完毕,我们来看看最后一个类JavaClassExecuter ,
它是提供给外部调用的入口,调用前面几个支持类组装逻辑,完成类加载工作。方法,如果期间出现任何异常,将异常信息打印到HackSystemout中,最后把缓冲区中的信息、作为方法的结果返回。JavaClassExecuter的实现代码如代运清单9-
7所示。
代码清单9-7
JavaClassExecuter的实现
public class JavaClassExecuter {
public static String execute(byte[] classByte) {
HackSystem.clearBuffer();
ClassModifier cm = new ClassModifier(classByte);
byte[] modiBytes = cm.modifyUTF8Constant("java/lang/System", "org/fenixsoft/classloading/execute/HackSystem");
HotSwapClassLoader loader = new HotSwapClassLoader();
Class clazz = loader.loadByte(modiBytes);
try {
Method method = clazz.getMethod("main", new Class[] { String[].class });
method.invoke(null, new String[] { null });
} catch (Throwable e) {
e.printStackTrace(HackSystem.out);
}
return HackSystem.getBufferString();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
验证
远程执行功能的编码到此就完成了,接下来就要检验一下我们的劳动成果了。如果只是测试的话,那么可以任意写一个Java类
,内容无所谓,只要向System.out输出信息即可,取名为TestClass,
同时放到服务器C盘的根目录中。然后,建立一个JSP文件并加入如代码清单9-
8所示的内容,就可以在浏览器中看到这个类的运行结果了。
java逻辑第九章_深入理解jvm-(第九章)类加载及执行子系统的案例与实战相关推荐
- 深入理解Java虚拟机(第二版) 第九章:类加载及执行子系统的案例与实战
第九章 类加载及执行子系统的案例与实战 9.1 概述 9.2 Tomcat: 正统的类加载器架构 9.3 OSGi:灵活的类加载器架构 9.4 字节码生成技术与动态代理的实现 9.5 Retrotra ...
- JVM——类加载及执行子系统的案例与实战(Tomcat)
摘要 本文将深入的学习与分析JVM虚拟机的原理和相关的调优的相关实例. 类加载及执行子系统的案例与实战 Tomcat: 正统的类加载器架构 主流的Java Web服务器, 如Tomcat. Jetty ...
- java visualvm远程监控_深入理解JVM虚拟机12:JVM性能管理神器VisualVM介绍与实战
本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...
- java虚拟机内存监控_深入理解JVM虚拟机9:JVM监控工具与诊断实践
本文转自: https://juejin.im/post/59e6c1f26fb9a0451c397a8c 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到 ...
- fegin需要实现类_深入理解JVM(六)--虚拟机类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类从被加载到虚拟机内存开始,到卸载出内存为止 ...
- 【深入理解JVM】:类加载器与双亲委派模型
转载自 [深入理解JVM]:类加载器与双亲委派模型 类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段" ...
- 商用短链平台_第8章_ 账号微服务注册模块+短信验证码+阿里云OSS开发实战
商用短链平台_第8章_ 账号微服务注册模块+短信验证码+阿里云OSS开发实战 文章目录 商用短链平台_第8章_ 账号微服务注册模块+短信验证码+阿里云OSS开发实战 第八章 账号微服务注册模块+短信验 ...
- java jvm垃圾回收算法_深入理解JVM虚拟机2:JVM垃圾回收基本原理和算法
本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 喜欢的话麻烦点下Star哈 文章将同步到我的个人博客: www.how ...
- java 句柄池_深入理解JVM之Java对象的创建、内存布局、访问定位详解
本文实例讲述了深入理解JVM之Java对象的创建.内存布局.访问定位.分享给大家供大家参考,具体如下: 对象的创建 一个简单的创建对象语句Clazz instance = new Clazz();包含 ...
最新文章
- main方法_错误: 在类 ZiFUChuan.Pyramid 中找不到 main 方法, 请将 main 方法定义为:
- Flutter开发之诊断布局调试工具:inspector(12)
- ant+svn+tomcat实现项目自动部署
- 【Android 应用开发】 ActionBar 样式详解 -- 样式 主题 简介 Actionbar 的 icon logo 标题 菜单样式修改
- 关于面试,我也有说的
- springmvc 配置和spring配置?
- Swift之深入解析可选链的功能和使用
- RabbitMQ, ZeroMQ, Kafka 是一个层级的东西吗, 相互之间有哪些优缺点
- mac obs 录屏黑屏_有了它,我把其他录屏软件都卸载了!
- java5 离线安装包_ElasticSearch 5.5 离线环境的完整安装及配置详情,附kibana、ik插件配置及安装包下载路径...
- 暨南大学人文社科a类期刊_关于调整人文社科B类和C类期刊目录的通知
- 数据库分页LIMIT
- SCC(五):ACT
- 无心剑汉英双语诗003. 《书海》
- Vscode Remote SSH 远程连接失败过程报错:试图写入的管道不存在
- 输入法的一些设置,以及解决 输入法 ctrl+c 等快捷键不能操作问题?
- 第一卷清晨的帝国 第一百五十一章 起步
- PHP之 微信支付 查询企业付款银行卡API 或 查询给企业付款是否到账 功能业务处理
- dubbo-admin0.3.0安装教程
- 【专题5: 硬件设计】 之 【16.二极管/三极管的钳位功能】
热门文章
- 自动登录126邮箱的脚本
- 输入对5层网络迭代次数的影响
- php 截取字符串的方法,php截取字符串的方法介绍
- 【Paper】2015_异构无人机群鲁棒一致性协议设计_孙长银
- Floats and marginpars not allowed inside `multicols' Unknown float option `H'. 基于LaTex+VSCode+MAC
- 【编程通识】正则表达式
- 【强化学习】DDPG
- 基于Simulink的高速跳频通信系统抗干扰性能分析
- python基础学习11----函数
- 目前中关村在线上面的CPU排行情况