【JVM学习笔记】类加载器
概述
类加载器用来把类加载到Java虚拟机中。从JDK1.2版本开始,类的加载过程采用父委托机制,这种机制能更好地保证Java平台的安全。在此委托机制中,除了Java虚拟机自带的根类加载器以外,其余的类加载器都有且只有一个父加载器。当Java程序请求加载器loader1加载sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载器能加载,则由父加载器完成加载任务,否则才由加载器loader1本身加载sample类。
加载器的分类
Java虚拟机自带了以下几种加载器:
- 根(Bootstrap)类加载器:该加载器没有父加载器。它负责加载虚拟机的核心类库,如java.lang.*等。例如java.lang.Object就是由根类加载器加载的。根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库。根类加载器的实现依赖于底层操作系统,属于虚拟机的实现的一部分,它并没有继承java.lang.ClassLoader类
- 扩展(Extension)类加载器:它的父加载器为根类加载器。它从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK安装目录的jre/lib/ext子目录(扩展目录)下加载类库,如果把用户创建的JAR文件放在这个目录下,也会自动由扩展类加载器加载。扩展类加载器是纯Java类,是java.lang.ClassLoader类的子类
- 系统(System)类加载器:也称应用类加载器,它的父加载器为扩展类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,它是用户自定义的类加载器的默认父加载器。系统类加载器是纯Java类,是java.lang.ClassLoader类的子类
除了以上虚拟机自带的加载器外,用户还可以定制自己的类加载器。Java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器都应该继承ClassLoader类。
(扩展类加载器和系统类加载器都是由启动类加载器加载的)
类加载器的父委托机制
在父委托机制中,各个加载器按照父子关系形成了树形结构,除了根类加载器之外,其余的类加载器都有且只有一个父加载器(题外话,双亲委托机制是hotspot默认提供的一种机制,但并不是所有环境中都遵循这种机制,OSGI就打破了这种机制)
如果有一个类加载器能够成功加载某个类,那么这个类加载器被称为定义类加载器,所有能成功返回Class对象引用的类加载器(包括定义类加载器)都被称为初始类加载器
获得ClassLoader的途径
获得当前类的ClassLoader
clazz.getClassLoader();
获得当前线程上下文的ClassLoader
Thread.currentThread().getContextClassLoader()
获得系统的ClassLoader
ClassLoader.getSystemClassLoader()
获得调用者的ClassLoader(这种用的比较少)
DriverManager.getCallerClassLoader()
数组类的类对象不是由类加载器创建的,而是根据Java运行时的需要自动创建的。 Class.getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同; 如果元素类型是基本类型,则数组类没有类加载器。
public class ClassLoaderLearn {
public static void main(String[] args) throws ClassNotFoundException {
String[] strings = new String[2];
System.out.println(strings.getClass().getClassLoader()); ClassLoaderLearn[] array = new ClassLoaderLearn[5];
System.out.println(array.getClass().getClassLoader()); int[] nums = new int[5];
System.out.println(nums.getClass().getClassLoader());
}
}
运行结果为
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
入门示例
package com.learn.jvm.loader; import java.io.*; /**
* @author wx
* @Description
* @date 2019/09/01 11:51
* 实际上我们自定义的这个ClassLoader只重写了findclass方法
*/
public class ClassLoaderLearn extends ClassLoader { private String classLoaderName; // classloader的名称
private String path; // 加载路径
private String fileName; // 文件名 public void setPath(String path) {
this.path = path;
} public void setFileName(String fileName) {
this.fileName = fileName;
} public ClassLoaderLearn(String classLoaderName) {
// 这句话可以省略,因为默认就会去调用父类的无参构造方法
// 在这里写出来就是为了强调它的作用,super()这个方法会去创建一个新的ClassLoader,其父加载器为SystemClassLoader
super();
this.classLoaderName = classLoaderName;
} public ClassLoaderLearn(ClassLoader parent, String classLoaderName) {
// super(parent)会创建一个新的类加载器,这个新的类加载器的父加载器为parent
super(parent);
this.classLoaderName = classLoaderName;
} @Override
public String toString() {
return "ClassLoaderLearn{" +
"classLoaderName='" + classLoaderName + '\'' +
'}';
} // 对父类中findClass方法doc文档的翻译:
// 查找具有指定二进制名称的类。 此方法应由遵循委托模型的类加载器实现覆盖以加载类,并且在检查所请求类的父类加载器之后将由loadClass方法调用。 默认实现抛出ClassNotFoundException
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
System.out.println("findClass invoked: " + className);
System.out.println("class loader name: " + this.classLoaderName);
byte[] data = this.loadClassData(className);
return this.defineClass(className, data, 0, data.length);
} // className 是要加载的类的binaryname
private byte[] loadClassData(String className) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null; try {
is = new FileInputStream(new File(path+fileName));
baos = new ByteArrayOutputStream(); int ch;
while (-1 != (ch = is.read())) {
baos.write(ch);
} data = baos.toByteArray(); } catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
} return data;
} public static void main(String[] args) throws Exception {
ClassLoaderLearn loader = new ClassLoaderLearn("MyClassLoader");
// loader.setPath("D:\\workspace-learn\\common-learn\\learn-jvm\\target\\classes");
loader.setPath("D:/");
loader.setFileName("Test.class"); // loadClass是ClassLoader提供的方法
// 深入源代码的doc文档,可以看到,loadClass方法按照如下顺序寻找类
// 1.调用findLoadedClass(String)方法检查这个类是否已经被加载过了
// 2.调用父加载器的loadClass方法,如果父加载器为null,就会使用根类加载器
// 3.调用findClass(String)方法来寻找这个类
Class<?> clazz = loader.loadClass("com.learn.jvm.loader.Test");
System.out.println("class: " + clazz);
Object obj = clazz.newInstance();
System.out.println("obj:" + obj);
System.out.println(obj.getClass().getClassLoader());
}
}
上述代码中的Test类,是项目路径中的一个类。如果不删除生成的class文件则Test将会由根类加载器加载,加载的系统路径下的Test.class
如果删除项目路径下的Test.class文件,将其放到D盘,运行上诉程序,则Test类将由我们自定义的类加载器加载,如下面输出结果:
findClass invoked: com.learn.jvm.loader.Test
class loader name: MyClassLoader
class: class com.learn.jvm.loader.Test
obj:com.learn.jvm.loader.Test@14ae5a5
ClassLoaderLearn{classLoaderName='MyClassLoader'}
命名空间
- 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
- 在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
- 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类
- (因此,子加载器所加载的类能够访问父加载器所加载的类,但父加载器加载的类无法访问到子加载器所加载的类)
(感觉OSGI就是这样的)
以下代码,在项目路径下有Test.class和项目路径没有Test.class这两种情况下,输出结果是不同的
public class ClassLoaderLearn extends ClassLoader { private String classLoaderName; // classloader的名称
private String path; // 加载路径
private String fileName; // 文件名 public void setPath(String path) {
this.path = path;
} public void setFileName(String fileName) {
this.fileName = fileName;
} public ClassLoaderLearn(String classLoaderName) {
// 这句话可以省略,因为默认就会去调用父类的无参构造方法
// 在这里写出来就是为了强调它的作用,super()这个方法会去创建一个新的ClassLoader,其父加载器为SystemClassLoader
super();
this.classLoaderName = classLoaderName;
} public ClassLoaderLearn(ClassLoader parent, String classLoaderName) {
// super(parent)会创建一个新的类加载器,这个新的类加载器的父加载器为parent
super(parent);
this.classLoaderName = classLoaderName;
} @Override
public String toString() {
return "ClassLoaderLearn{" +
"classLoaderName='" + classLoaderName + '\'' +
'}';
} // 对父类中findClass方法doc文档的翻译:
// 查找具有指定二进制名称的类。 此方法应由遵循委托模型的类加载器实现覆盖以加载类,并且在检查所请求类的父类加载器之后将由loadClass方法调用。 默认实现抛出ClassNotFoundException
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
byte[] data = this.loadClassData(className);
return this.defineClass(className, data, 0, data.length);
} // className 是要加载的类的binaryname
private byte[] loadClassData(String className) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null; try {
is = new FileInputStream(new File(path+fileName));
baos = new ByteArrayOutputStream(); int ch;
while (-1 != (ch = is.read())) {
baos.write(ch);
} data = baos.toByteArray(); } catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
} return data;
} public static void main(String[] args) throws Exception {
ClassLoaderLearn loader1 = new ClassLoaderLearn("loader1");
loader1.setPath("D:/");
loader1.setFileName("Test.class"); Class<?> clazz1 = loader1.loadClass("com.learn.jvm.loader.Test");
System.out.println("class hashcode: " + clazz1.hashCode());
Object obj1 = clazz1.newInstance();
System.out.println("obj:" + obj1);
System.out.println("classloader: " + obj1.getClass().getClassLoader()); System.out.println("=========================="); ClassLoaderLearn loader2 = new ClassLoaderLearn("loader2");
loader2.setPath("D:/");
loader2.setFileName("Test.class"); Class<?> clazz2 = loader2.loadClass("com.learn.jvm.loader.Test");
System.out.println("class hashcode: " + clazz2.hashCode());
Object obj2 = clazz2.newInstance();
System.out.println("obj:" + obj2);
System.out.println("classloader: " + obj2.getClass().getClassLoader());
}
}
输出结果(项目路径下有Test.class)
class hashcode: 356573597
obj:com.learn.jvm.loader.Test@677327b6
classloader: sun.misc.Launcher$AppClassLoader@18b4aac2
==========================
class hashcode: 356573597
obj:com.learn.jvm.loader.Test@14ae5a5
classloader: sun.misc.Launcher$AppClassLoader@18b4aac2
输出结果(项目路径下没有Test.class)
class hashcode: 21685669
obj:com.learn.jvm.loader.Test@7f31245a
classloader: ClassLoaderLearn{classLoaderName='loader1'}
==========================
class hashcode: 1173230247
obj:com.learn.jvm.loader.Test@330bedb4
classloader: ClassLoaderLearn{classLoaderName='loader2'}
这是为什么呢,为什么Test类被加载了两次。这就涉及到类加载器的命名空间问题,由于loader1和loader2在双亲委托机制中没有委托关系,所以是属于两个不同的命名空间
类的卸载
- 当一个类被加载、连接和初始化后,它的生命周期就开始了。当这个类的Class对象不再被引用,即不可抵达时,Class对象就会结束生命周期,这个类在方法区的数据也会被卸载,从而结束这个类的生命周期
- 一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期
由Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。前面已经介绍过,Java虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象,因此这些Class对象始终是可触及的
由用户自定义的类加载器所加载的类是可以被卸载的
在类加载器的内部实现中,用一个Java集合来存放所加载类的引用。另一方面,一个Class对象总是会引用它的类加载器,调用Class对象的getClassLoader()方法,就能获得它的类加载器。由此可见,类的Class对象与类加载器之间为双向关联关系。
public class ClassLoaderLearn extends ClassLoader { private String classLoaderName; // classloader的名称
private String path; // 加载路径
private String fileName; // 文件名 public void setPath(String path) {
this.path = path;
} public void setFileName(String fileName) {
this.fileName = fileName;
} public ClassLoaderLearn(String classLoaderName) {
// 这句话可以省略,因为默认就会去调用父类的无参构造方法
// 在这里写出来就是为了强调它的作用,super()这个方法会去创建一个新的ClassLoader,其父加载器为SystemClassLoader
super();
this.classLoaderName = classLoaderName;
} public ClassLoaderLearn(ClassLoader parent, String classLoaderName) {
// super(parent)会创建一个新的类加载器,这个新的类加载器的父加载器为parent
super(parent);
this.classLoaderName = classLoaderName;
} @Override
public String toString() {
return "ClassLoaderLearn{" +
"classLoaderName='" + classLoaderName + '\'' +
'}';
} // 对父类中findClass方法doc文档的翻译:
// 查找具有指定二进制名称的类。 此方法应由遵循委托模型的类加载器实现覆盖以加载类,并且在检查所请求类的父类加载器之后将由loadClass方法调用。 默认实现抛出ClassNotFoundException
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
byte[] data = this.loadClassData(className);
return this.defineClass(className, data, 0, data.length);
} // className 是要加载的类的binaryname
private byte[] loadClassData(String className) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null; try {
is = new FileInputStream(new File(path+fileName));
baos = new ByteArrayOutputStream(); int ch;
while (-1 != (ch = is.read())) {
baos.write(ch);
} data = baos.toByteArray(); } catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
} return data;
} public static void main(String[] args) throws Exception {
ClassLoaderLearn loader1 = new ClassLoaderLearn("loader1");
loader1.setPath("D:/");
loader1.setFileName("Test.class"); Class<?> clazz1 = loader1.loadClass("com.learn.jvm.loader.Test");
System.out.println("class hashcode: " + clazz1.hashCode());
Object obj1 = clazz1.newInstance();
System.out.println("obj:" + obj1);
System.out.println("classloader: " + obj1.getClass().getClassLoader()); System.out.println("==========================");
loader1 = null;
obj1 = null;
clazz1 = null;
System.gc();
Thread.sleep(200000); loader1 = new ClassLoaderLearn("loader1");
loader1.setPath("D:/");
loader1.setFileName("Test.class"); clazz1 = loader1.loadClass("com.learn.jvm.loader.Test");
System.out.println("class hashcode: " + clazz1.hashCode());
obj1 = clazz1.newInstance();
System.out.println("obj:" + obj1);
System.out.println("classloader: " + obj1.getClass().getClassLoader());
}
}
输出结果(注意,项目路径下已经删掉了Test.class)
class hashcode: 21685669
obj:com.learn.jvm.loader.Test@7f31245a
classloader: ClassLoaderLearn{classLoaderName='loader1'}
==========================
[Unloading class com.learn.jvm.loader.Test 0x00000007c0061828]
class hashcode: 1173230247
obj:com.learn.jvm.loader.Test@330bedb4
classloader: ClassLoaderLearn{classLoaderName='loader1'}
使用jvisualvm看到的结果如下
如果A类的构造函数里创建了B类对象,那么B类和A类在加载的时候,是被同一个加载器加载
定义一个MyCat类
public class MyCat {
public MyCat() {
System.out.println("MyCat is loaded by " + this.getClass().getClassLoader());
}
}
定义一个MySample类
public class MySample {
public MySample() {
System.out.println("MySample is loaded by: " + this.getClass().getClassLoader());
new MyCat();
}
}
然后是我们的类加载器和入口函数
public class ClassLoaderLearn extends ClassLoader { private String classLoaderName; // classloader的名称
private String path; // 加载路径
private String fileName; // 文件名 public void setPath(String path) {
this.path = path;
} public void setFileName(String fileName) {
this.fileName = fileName;
} public ClassLoaderLearn(String classLoaderName) {
// 这句话可以省略,因为默认就会去调用父类的无参构造方法
// 在这里写出来就是为了强调它的作用,super()这个方法会去创建一个新的ClassLoader,其父加载器为SystemClassLoader
super();
this.classLoaderName = classLoaderName;
} public ClassLoaderLearn(ClassLoader parent, String classLoaderName) {
// super(parent)会创建一个新的类加载器,这个新的类加载器的父加载器为parent
super(parent);
this.classLoaderName = classLoaderName;
} @Override
public String toString() {
return "ClassLoaderLearn{" +
"classLoaderName='" + classLoaderName + '\'' +
'}';
} // 对父类中findClass方法doc文档的翻译:
// 查找具有指定二进制名称的类。 此方法应由遵循委托模型的类加载器实现覆盖以加载类,并且在检查所请求类的父类加载器之后将由loadClass方法调用。 默认实现抛出ClassNotFoundException
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
byte[] data = this.loadClassData(className);
return this.defineClass(className, data, 0, data.length);
} // className 是要加载的类的binaryname
private byte[] loadClassData(String className) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null; try {
is = new FileInputStream(new File(path+fileName));
baos = new ByteArrayOutputStream(); int ch;
while (-1 != (ch = is.read())) {
baos.write(ch);
} data = baos.toByteArray(); } catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
} return data;
} public static void main(String[] args) throws Exception {
ClassLoaderLearn loader1 = new ClassLoaderLearn("loader1");
loader1.setPath("D:/");
loader1.setFileName("MySample.class"); Class<?> clazz1 = loader1.loadClass("com.learn.jvm.loader.MySample");
System.out.println("class hashcode: " + clazz1.hashCode());
loader1.setFileName("MyCat.class");
Object obj1 = clazz1.newInstance();
}
}
运行结果(未删除项目路径下MyCat和MySample的情况)
class hashcode: 356573597
MySample is loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2
MyCat is loaded by sun.misc.Launcher$AppClassLoader@18b4aac2
运行结果(已删除项目路径下MyCat和MySample的情况,将MyCat和MySample放在了D盘 )
class hashcode: 21685669
MySample is loaded by: ClassLoaderLearn{classLoaderName='loader1'}
MyCat is loaded by ClassLoaderLearn{classLoaderName='loader1'}
如果在项目路径下只有MySample.class,没有MyCat.class
而在D盘两者都有,运行以上程序,输出结果为
class hashcode: 356573597
MySample is loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: com/learn/jvm/loader/MyCat
at com.learn.jvm.loader.MySample.<init>(MySample.java:11)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at com.learn.jvm.loader.ClassLoaderLearn.main(ClassLoaderLearn.java:95)
Caused by: java.lang.ClassNotFoundException: com.learn.jvm.loader.MyCat
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
这是因为我们定义的类加载器,没有指定其父加载器,默认父加载器为系统类加载器。系统类加载器能够在classpath下面找到MySample类,但是接着却找不到MyCat类了
又一个例子,保持ClassLoaderLearn 类不变,新增一个Person类如下
public class Person {
private Person myPerson; public void setMyPerson(Object obj) {
this.myPerson = (Person)obj;
}
}
修改main函数如下
public static void main(String[] args) throws Exception {
ClassLoaderLearn loader1 = new ClassLoaderLearn("loader1");
ClassLoaderLearn loader2 = new ClassLoaderLearn("loader2");
loader1.setPath("D:/");
loader1.setFileName("Person.class");
loader2.setPath("D:/");
loader2.setFileName("Person.class");
Class<?> clazz1 = loader1.loadClass("com.learn.jvm.loader.Person");
Class<?> clazz2 = loader2.loadClass("com.learn.jvm.loader.Person"); Object obj1 = clazz1.newInstance();
Object obj2 = clazz2.newInstance(); Method method = clazz1.getMethod("setMyPerson", Object.class);
method.invoke(obj1,obj2);
}
输出结果(项目路径未删除Person.class)
(无输出,程序正常结束)
输出结果(项目路径已删除Person.class并放到了D盘)
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.learn.jvm.loader.ClassLoaderLearn.main(ClassLoaderLearn.java:100)
Caused by: java.lang.ClassCastException: com.learn.jvm.loader.Person cannot be cast to com.learn.jvm.loader.Person
at com.learn.jvm.loader.Person.setMyPerson(Person.java:12)
... 5 more
这是因为这两个类是由两个不同命名空间里的loader加载的,不是同一个类对象
【JVM学习笔记】类加载器的更多相关文章
- JVM学习笔记——类加载器与类加载过程
类加载器与类加载过程 类加载器ClassLoader 类加载器 ClassLoader 用于把 class 文件装载进内存. 启动类加载器(Bootstrap ClassLoader): 这个类加载使 ...
- JVM学习笔记——类加载过程
JVM学习笔记——类加载过程 类加载模型——双亲委派模型(Parents Delegation Model)也可称为“溯源委派加载模型” Java的类加载器是一个运行时核心基础设施模块,主要是启动之初 ...
- JVM学习笔记——类加载和字节码技术篇
JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- JVM学习--(六)类加载器原理
我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...
- JVM学习记录-类加载器
前言 JVM设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外面去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类 ...
- [jvm学习笔记]-类加载过程
JVM类加载的过程 加载=>验证=>准备=>解析=>初始化 5个阶段所执行的具体动作 加载 在加载阶段,虚拟机需要完成3个事情1.通过一个类的全限定名获取定义此类的二进制字节流 ...
- JVM学习笔记:虚拟机的类加载机制
JVM类加载机制分两部分来总结: (1)类加载过程 (2)类加载器 一.JVM类加载过程 类的加载过程:加载 →连接(验证 → 准备 → 解析)→ 初始化. 类的生命周期:加载 →连接(验证 → 准备 ...
- java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)
java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...
- JVM学习笔记-第七章-虚拟机类加载机制
JVM学习笔记-第七章-虚拟机类加载机制 7.1 概述 Java虚拟机描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被 ...
随机推荐
- 网络协议相关面试问题-https加密算法相关面试问题
密钥: 密钥是一种参数,它是在使用密码cipher算法过程中输入的参数,同一个明文在相同的密码算法和不同的密钥计算下会产生不同的密文.所以说算法既使公开了但是密钥参数不同其结果也是不同的,其中关于明文 ...
- python 获取安装包apk, ipa 信息
# -*- coding:utf-8 -*- import re import os import zipfile from biplist import * from androguard.core ...
- 使用google身份验证器实现动态口令验证
最近有用户反应我们现有的短信+邮件验证,不安全及短信条数限制和邮件收验证码比较慢的问题,希望我们 也能做一个类似银行动态口令的验证方式.经过对可行性的分析及慎重考虑,可以实现一个这样的功能. 怎么实现 ...
- Java WEB框架——SSM之Hello world
一.建立项目 先搭建一个webapp项目(要选creat from arctype) 起名 选择maven路径,settings.xml那里要选择override才可以在资源管理器中浏览.接下来直接N ...
- docker及k8s安装consul
一.docker部署consul集群 参考文献:https://www.cnblogs.com/lonelyxmas/p/10880717.html https://blog.csdn.net/qq_ ...
- @SpringQueryMap注解 feign的get传参方式(转)
spring cloud项目使用feign的时候都会发现一个问题,就是get方式无法解析对象参数.其实feign是支持对象传递的,但是得是Map形式,而且不能为空,与spring在机制上不兼容,因此无 ...
- 配置并访问NFS共享
NFS服务器 192.168.2.5 NFS客户机 192.168.2.100 软件包nfs-utils用来提供NFS共享服务及相关工具,而软件包rpcbind用来提供RPC协议的支持 服务器 修改/ ...
- jquery fadeIn()方法 语法
jquery fadeIn()方法 语法 作用:fadeIn() 方法使用淡入效果来显示被选元素,假如该元素是隐藏的.大理石平台检定规程 语法:$(selector).fadeIn(speed,cal ...
- noi.ac #531 神树和物品
题目链接:戳我 决策单调性 (蒟蒻终于会写决策单调性啦!考试全场切这题就我不会啊嘤) (证明?不会啊,自己打表看QAQ) 44pts \(O(n^2)\)代码: #include<iostrea ...
- USACO19JAN Redistricting
题目链接:戳我 一个优先队列优化DP 一定要注意第二关键字的排序啊!!我真的是菜,被坑了好久qwq 设\(f[i]\)表示前i个的最小答案,从前面选择的时候第一关键字是f[j]的大小,第二关键字是要确 ...