Skip to content

Commit a909ffd

Browse files
committed
ignore
1 parent 209b2e8 commit a909ffd

8 files changed

Lines changed: 447 additions & 2 deletions
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
------------------------
2+
ClassLoader |
3+
------------------------
4+
# 类加载器, 是一个抽象类
5+
6+
# 构造函数
7+
protected ClassLoader()
8+
protected ClassLoader(ClassLoader parent)
9+
10+
# 静态方法
11+
protected static boolean registerAsParallelCapable()
12+
protected final void resolveClass(Class<?> c)
13+
14+
public static ClassLoader getSystemClassLoader()
15+
public static URL getSystemResource(String name)
16+
public static InputStream getSystemResourceAsStream(String name)
17+
public static Enumeration<URL> getSystemResources(String name)
18+
19+
# 实例函数
20+
21+
@Deprecated
22+
protected final Class<?> defineClass(byte[] b, int off, int len)
23+
24+
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
25+
protected final Class<?> defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)
26+
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b, ProtectionDomain protectionDomain)
27+
28+
protected Package definePackage(String name, String specTitle,String specVersion, String specVendor, String implTitle, String implVersion,String implVendor, URL sealBase)
29+
30+
protected Class<?> findClass(String name)
31+
protected String findLibrary(String libname)
32+
protected final Class<?> findLoadedClass(String name)
33+
protected URL findResource(String name)
34+
protected Enumeration<URL> findResources(String name)
35+
protected final Class<?> findSystemClass(String name)
36+
protected Object getClassLoadingLock(String className
37+
protected Package getPackage(String name)
38+
protected Package[] getPackages()
39+
protected Class<?> loadClass(String name, boolean resolve)
40+
public Class<?> loadClass(String name)
41+
42+
protected final void setSigners(Class<?> c, Object[] signers)
43+
44+
45+
public void clearAssertionStatus()
46+
public final ClassLoader getParent()
47+
public URL getResource(String name)
48+
public InputStream getResourceAsStream(String name)
49+
public Enumeration<URL> getResources(String name)
50+
51+
public void setClassAssertionStatus(String className, boolean enabled)
52+
public void setDefaultAssertionStatus(boolean enabled)
53+
public void setPackageAssertionStatus(String packageName, boolean enabled)
54+
55+
56+
57+
58+
59+

‎Jvm/jvm-gc.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,7 @@ public static void main(String[] args) {
208208

209209

210210
# 安全区域
211-
211+
* 在没有分配到CPU时间的线程, 典型的就是: sleep,wait 状态的线程, 它们没法跑到安全点
212+
* 安全区域就是, 在一段儿代码中, 引用关系不会发生变化, 在这个区域中的任何地方开始GC都是安全的
213+
214+

‎Jvm/jvm-load-类加载器.java‎

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
-----------------
2+
ClassLoader |
3+
-----------------
4+
# 类和类加载器
5+
* 每个类, 都要和加载它的加载器一起, 确立在jvm中的唯一性
6+
7+
* 比较两个类是否相等, 只有在这两个类都是同一个类加载器加载的前提下才有意义
8+
* 否则, 一个class文件, 被一个jvm加载, 但是存在两个加载器进行加载, 那么这俩加载后的类就肯定不相等
9+
10+
* 这里的相等包括了 Class 对象的: equals(), isAssignableForm(), instance() 返回的结果
11+
* 也包括使用关键字: instanceof 判断结果
12+
13+
14+
15+
16+
# 类加载类型
17+
* 启动类加载器 Boostrap ClassLoader
18+
* 在 $JAVA_HOME\lib 目录中, 或者通过 -Xbootclasspath 参数指定
19+
* 使用 C++ 实现, 是JVM自身的一部分, 不能被程序代码获取到
20+
21+
* 扩展类加载器 Extension ClassLoader
22+
* 由: sun.misc.Launcher$ExtClassLoader 实现, 实现接口:ClassLoader
23+
* 它负责加载 $JAVA_HOME\lib\ext 目录中的, 或者被 java.ext.dirs 系统变量指定的路径中的所有类库
24+
* 开发者可以直接使用扩展类加载器
25+
26+
* 应用类加载器 Application ClassLoader
27+
* 由: sun.misc.Launcher$AppClassLoader 实现, 实现接口:ClassLoader
28+
* 由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值, 所以也被称为系统类加载器
29+
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
30+
31+
* 它负责加载用户类路径(CLASS_PATH)上所指定的类库
32+
* 如果程序中没自定义过类加载器, 一般情况下这个就是默认的类加载器
33+
34+
Class<?> driverClass = Class.forName("com.mysql.cj.jdbc.Driver");
35+
System.out.println(driverClass.getClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2
36+
37+
38+
39+
# 双亲委派机制
40+
* 双亲委派机制, 要求除了启动类加载器以外, 都必须要有父类加载器
41+
* 子父关系并不是由继承实现的, 而是使用组合的方式实现的
42+
43+
* 如果一个类加载器收到了类加载请求, 它首先会把这个请求委托给父类加载器去完成
44+
* 每一个类加载器都是如此, 因此最后会委托到启动类加载器进行加载
45+
* 父级启动类加载器成功加载, 那么就直接返回加载后的结果
46+
* 如果父类无法加载, 就尝试自己加载, 如果加载失败, 则抛出异常: ClassNotFoundException
47+
48+
* 这种机制的好处在于, 你没法使用自己的加载器,加载自己定义的那些与系统类重名的类, 安全
49+
* ClassLoader 的 loadClass(String name) 方法实现代码
50+
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {
51+
synchronized (getClassLoadingLock(name)) {
52+
// 首先, 检查类是否被加载过
53+
Class<?> c = findLoadedClass(name);
54+
if (c == null) {
55+
long t0 = System.nanoTime();
56+
try {
57+
if (parent != null) {
58+
c = parent.loadClass(name, false);
59+
} else {
60+
c = findBootstrapClassOrNull(name);
61+
}
62+
} catch (ClassNotFoundException e) {
63+
// 父级加载器抛出了 ClassNotFoundException , 说明父级加载失败
64+
}
65+
66+
if (c == null) {
67+
// 父级加载器, 加载失败的情况下, 调用本身的 findClass() 进行加载
68+
69+
long t1 = System.nanoTime();
70+
c = findClass(name);
71+
72+
// 定义的类加载器的记录统计数据
73+
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
74+
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
75+
sun.misc.PerfCounter.getFindClasses().increment();
76+
}
77+
}
78+
if (resolve) {
79+
resolveClass(c);
80+
}
81+
return c;
82+
}
83+
}
84+
85+
* 就算是自己定义了类加载器, 使用 defineClass() 去加载一个以 "java.lang" 开头的类, 也不会成功
86+
* 会得到JVM抛出的安全异常
87+
88+
# 破坏双亲委托机制
89+
* 双亲委派机制, 并不是强制性的, 而是推荐给开发者的加载器实现模式
90+
91+
* 双亲委派机制第一次被破坏, 是因为历史遗留问题
92+
* 该机制在 jdk1.2 才被引入, 但是类加载器和抽象类: ClassLoader 在 jdk1.0 就存在
93+
* 对于那些, 已经存在的用户自定义加载器实现, 不得不做出妥协,
94+
95+
* 为了向前兼容, JDK1.2 后在 ClassLoader 添加了一个方法: protected Class<?> findClass(String name)
96+
* 在此之前, 用于继承 ClassLoader 方法, 唯一目的就是为了覆写: loadClass() 方法, 因为JVM在进行类加载的时候会调用私有方法:private Class<?> loadClassInternal(String name)
97+
* 而这个方法的唯一逻辑就是去调用自己的: loadClass() 方法
98+
99+
* JDK1.2后不建议覆写: loadClass() 方法, 而是把加载逻辑写到 findClass() 方法中
100+
* loadClass() 方法里面的逻辑, 当父加载器加载失败, 才会调用 findClass() 完成加载
101+
* 这样的规则是符合双亲委派机制的
102+
103+
104+
105+
* 双亲委派机制第二次被破坏, 是因为这个模型自身的缺陷导致的
106+
* 该机制很好的解决了基础类的统一加载的问题(越是基础的类,越由最上层的加载器进行加载)
107+
108+
* 但是基础的类, 可能需要回调用户的代码, 例如: JNDI
109+
* JNDI的目的是最资源进行集中管理和查找, 它需要调用由厂商实现, 并且部署在classpath下的JNDI接口提供者的代码
110+
* 但是启动类加载器, 不认识这些代码
111+
112+
* 为了解决这个问题, Java设计团队鼓捣了一个 线程上下文加载: Thread Context ClassLoader(其实设计得不太优雅)
113+
* Thread 的实例有一个方法:public void setContextClassLoader(ClassLoader cl)
114+
* 如果创建线程的时候, 该加载器还没设置, 它将会从父级线程继承一个, 如果应用程序全局范围内都没设置过的话, 那么这个类加载器默认就是应用程序类加载器
115+
116+
* JDNI服务使用这个线程上线我的加载器去加载所需要的SPI代码, 也就是父类加载器请求子类加载器去完成类加载
117+
* 这种行为, 实际上就是打通了双亲委派模型的层次结构来逆向使用加载器
118+
* 已经违背了双亲委派模型的一般性原则, 但是没则, JAVA涉及 SPI的加载动作, 都基本才用这种方式
119+
120+
121+
122+
* 双亲委派机制的第三次被破坏, 是因为对程序的动态性追求导致的
123+
* 动态性: 代码热替换, 模块热部署
124+
* 目前OSGi已经是业务事实上的Java模块标准, 而OSGi实现模块化热部署的关键就是它自定义的类加载器机制的实现
125+
126+
* 每一个程序模块都有一个自己的类加载器, 当需要更换一个 Bundle 的时候, 就把 Bundle 连加载器一起换掉, 实现代码的热替换
127+
* 在OSGi环境下, 类加载器不是双亲委派模型中的树结构,而是更加复杂的网状结构
128+
129+
* OSGi加载类的搜索顺序
130+
1. 把 java.* 开头的类委派给父类加载器加载
131+
2. 否则, 把委派列表名单内的类委派给父类加载器加载
132+
3. 否则, 把 import 列表中的类委派给 Export 这个类的 Bundle 的类加载器加载
133+
4. 否则, 查找当前 Bundle 的 ClassPath, 使用自己的类加载器加载
134+
5. 否则, 查找类是否在自己的 Fragment Bundle 中, 如果在, 则委托给 Fragment Bundle 的类加载器加载
135+
6. 否则, 查找 Dynamic import 列表的 Bundle, 委派给对应的 Bundle 的类加载器加载
136+
7. 否则, 类查找失败
137+
138+
* 只有开头2点符合双亲委派规则, 其余的类查找都是在平级的类加载器中进行的
139+
140+
141+
142+
143+
144+
145+
146+

‎Jvm/jvm-load-过程.java‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
------------------------
2+
类加载过程 |
3+
------------------------
4+
# 加载
5+
* 类的加载阶段需要做3件事情
6+
7+
1. 通过一个类的全限定名来获取定义该类的字节数据
8+
* 不仅仅是磁盘的class文件
9+
* 可以从ZIP包读取, (jar/war,ear)
10+
* 从可以网络读取,Applet
11+
* 运行时计算生成, Proxy 代理,就是使用: ProxyGenerator.generateProxyClass 来为特定的接口生成形式为"*$Proxy"的代理类字节数据
12+
* 可以由其他的文件生成, JSP, 通过JSP生成类
13+
* 可以从数据库读取
14+
15+
2. 把这个字节数据代表的静态存储结构转换为方法区的运行时数据结构
16+
17+
3. 在内存中生成一个代表该类的 Class 对象, 作为方法区访问这个类的各种数据的入口
18+
* Class 对象比较特殊, 虽然它是对象, 但是它是在方法区中的
19+
20+
21+
22+
* 对于数组而言, 加载有所不同, 数组类本身不需要通过类加载器创建
23+
* 它是由jvm直接创建的, 但是数组类和类加载器还是有密切的关系, 因为数组的元素类型, 最终是要靠类加载器去创建
24+
* 一个数组的创建过程需要遵循的规则
25+
- 如果数组的类型(去掉一个维度后的类型), 是引用类型就采取上述的加载过程
26+
27+
- 如果数组的类型不是引用类型(int[]), JVM会把数组标记为与引导加载器关联
28+
29+
30+
# 验证
31+
* 连接阶段的第一步
32+
* 验证的目的是为了确保 Class 文件的字节流中包含的信息符合当前JVM虚拟机的要求, 并且不会危害JVM的安全
33+
34+
* 验证阶段大致会完成4个阶段的校验动作
35+
1. 文件格式验证
36+
* 是否以魔输:0xCAFEBABE 开头
37+
* 主,次版本号是否在当前JVM虚拟机的处理范围内
38+
* 常量池的常量是否头不被支持的常量类型(检查常量 tag 标志)
39+
* CONSTANT_Utf8_info, 型的常量中是否有不符合 utf8 编码的数据
40+
* Class 文件中各个部分及文件本身是否有被删除, 或者附加的其他信息
41+
* ... 更多
42+
43+
2. 元数据验证
44+
* 是否有父类(除了 Object 外, 都应该有父类)
45+
* 是否继承了不允许被继承的类(final 修饰的类)
46+
* 如果类不是抽象类, 是否实现了其父类接口中的所有抽象方法
47+
* 类中的字段, 方法是否与父类产生矛盾(覆盖了父类的 final 字段, 或者出现了不符合规则的重载)
48+
49+
3. 字节码验证
50+
51+
4. 符合引用验证
52+
* 符号引用中通过字符串描述的全限定名, 是否能找到对应的类
53+
* 在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段
54+
* 符号引用中的类, 字段, 方法的访问属性:private,protected,public,default 是否可以被当前类访问
55+
56+
57+
# 准备
58+
* 为变量分配内存, 并且设置类变量(static)初始值的阶段
59+
* 这些变量所使用的内存都会在方法区中进行分配
60+
61+
public static int value = 123;
62+
63+
* 在准备阶段, value的初始化值是0, 因为还没开始执行任何Java方法
64+
* 把value赋值为 123 的putstatic指令是在程序被编译后, 存放于类构造器 <cinit>() 方法中
65+
66+
* 如果是 final 修饰的属性, 在准备阶段, 变量就会被初始化
67+
public static final int value = 123;
68+
69+
* 准备阶段, jvm会把value初始化为 123
70+
71+
# 解析
72+
73+
# 初始化
74+
* 类加载的最后一步, 直到该阶段, 才真正开始执行类中定义的 Java 程序代码
75+
76+
* <cinit>() 方法是由编译器自动收集类中的所有类变量的赋值动作,和静态语句块中的语句合并产生的
77+
* 编译器的收集顺序, 是按照源代码文件中出现的顺序所决定的
78+
79+
* static 代码块, 只能访问到定义在静态代码块之前的变量, 定义在之后的变量, 可以赋值, 但是不能访问
80+
class Foo {
81+
static {
82+
value = 10; // 赋值正常
83+
System.out.println(value);// 访问异常:非法向前引用
84+
}
85+
public static int value = 0;
86+
}
87+
88+
89+
* JVM保证子类的 <cinit>() 执行之前, 父类的<cinit>() 已经执行完毕
90+
* 因此, 父类的 static 会先执行
91+
* 因此在JVM中第一个执行 <cinit>() 肯定是 Object
92+
93+
* <cinit>() 对于接口来说并不是必须的, 如果一个类没有 static 代码块, 也没有对变量的赋值操作, 那么是可以不需要 <cinit>() 的
94+
95+
* <cinit>() 的初始化过程是线程安全的, 类加载的过程是线程安全的
96+
97+

0 commit comments

Comments
 (0)