JVM自定义类加载器加载指定classPath下的所有clas(3)

四、类Class卸载
  JVM中class和Meta信息存放在PermGen space区域(JDK1.8之后存放在MateSpace中)。如果加载的class文件很多,那么可能导致元数据空间溢出。引起java.lang.OutOfMemory异常。对于有些Class我们可能只需要使用一次,就不再需要了,也可能我们修改了class文件,我们需要重新加载 newclass,那么oldclass就不再需要了。所以需要在JVM中卸载(unload)类Class。
  JVM中的Class只有满足以下三个条件,才能被GC回收,也就是该Class被卸载(unload):

该类所有的实例都已经被GC。
该类的java.lang.Class对象没有在任何地方被引用。
加载该类的ClassLoader实例已经被GC。
  很容易理解,就是要被卸载的类的ClassLoader实例已经被GC并且本身不存在任何相关的引用就可以被卸载了,也就是JVM清除了类在方法区内的二进制数据。
  JVM自带的类加载器所加载的类,在虚拟机的生命周期中,会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象。因此这些Class对象始终是可触及的,不会被卸载。而用户自定义的类加载器加载的类是可以被卸载的。虽然满足以上三个条件Class可以被卸载,但是GC的时机我们是不可控的,那么同样的我们对于Class的卸载也是不可控的。

五、JVM自定义类加载器加载指定classPath下的所有class及jar
  经过以上几个点的说明,现在可以实现JVM自定义类加载器加载指定classPath下的所有class及jar了。这里没有限制class和jar的位置,只要是classPath路径下的都会被加载进JVM,而一些web应用服务器加载是有限定的,比如tomcat加载的是每个应用classPath+“/classes”加载class,classPath+“/lib”加载jar。以下就是代码啦...

package com.chenerzhu.learning.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**

@author chenerzhu

@create 2018-10-04 12:24
**/
public class ClassPathClassLoader extends ClassLoader{

private static Map<String, byte[]> classMap = new ConcurrentHashMap<>();
private String classPath;

public ClassPathClassLoader() {
}

public ClassPathClassLoader(String classPath) {
if (classPath.endsWith(File.separator)) {
this.classPath = classPath;
} else {
this.classPath = classPath + File.separator;
}
preReadClassFile();
preReadJarFile();
}

public static boolean addClass(String className, byte[] byteCode) {
if (!classMap.containsKey(className)) {
classMap.put(className, byteCode);
return true;
}
return false;
}

/**

这里仅仅卸载了myclassLoader的classMap中的class,虚拟机中的

Class的卸载是不可控的

自定义类的卸载需要MyClassLoader不存在引用等条件

@param className

@return
*/
public static boolean unloadClass(String className) {
if (classMap.containsKey(className)) {
classMap.remove(className);
return true;
}
return false;
}

/**

遵守双亲委托规则br/>*/
@Override
protected Class<?> findClass(String name) {
try {
byte[] result = getClass(name);
if (result == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, result, 0, result.length);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

private byte[] getClass(String className) {
if (classMap.containsKey(className)) {
return classMap.get(className);
} else {
return null;
}
}

private void preReadClassFile() {
File[] files = new File(classPath).listFiles();
if (files != null) {
for (File file : files) {
scanClassFile(file);
}
}
}

private void scanClassFile(File file) {
if (file.exists()) {
if (file.isFile() && file.getName().endsWith(".class")) {
try {
byte[] byteCode = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
String className = file.getAbsolutePath().replace(classPath, "")
.replace(File.separator, ".")
.replace(".class", "");
addClass(className, byteCode);
} catch (IOException e) {
e.printStackTrace();
}
} else if (file.isDirectory()) {
for (File f : file.listFiles()) {
scanClassFile(f);
}
}
}
}

private void preReadJarFile() {
File[] files = new File(classPath).listFiles();
if (files != null) {
for (File file : files) {
scanJarFile(file);
}
}
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/08b465dc31dad0a31c4a44df668df91d.html