只有在满足以下三个条件时,JVM的Class才会被GC回收: 也就是说,该类将被卸载。
-此类的所有实例都已经GC。 也就是说,JVM中不存在Class的实例。
-加载此类的类加载器已经GC。
-此类中的Java.lang.Class对象没有从任何地方引用。 例如,如果无法从任何地方通过反射访问类的方法,则
jsp和java类是完全不同的概念。
JSP-servlet在web容器中,而您的servlet是单实例、无状态、线程安全的。 也就是说,只有一个对象,
jsp更改后,web容器只需更新相应的servlet对象即可。
那么java呢?
这个类在你的APP应用程序中可能有n个实例。 这些实例是单向的,双向相关的还有n个实例。 如果你修改了,这些jvm所在的旧实例对象怎么办?
像AVA这样的静态语言,得不到像asp、php、jsp那样的效果。
weblogic散热原理
Weblogic允许在wls运行时部署组件的新版本。 这个过程称为热配置。 由于Javaclassloader没有删除一组现有类的机制,也无法用类的新版本替换旧版本,因此要在运行的虚拟机上更新相关类,请替换classloader 如果被替换,所有加载的类和派生的子classloader也会重新加载。 这些类的所有实例也必须重新加载。 在wls中,每个APP应用程序组件都有分层的classloaders。 它们是system classloader的子类,可以单独重新加载每个APP应用程序或部分APP应用程序,而不影响其他组件。
类加载器类型:
bootstrap类加载器/启动类加载器
主要负责使用jdk_home/lib目录下的核心api或-Xbootclasspath选项指定的jar包装。
扩展类加载器/扩展类加载器
主要负责jdk_home/lib/ext目录下的jar包或-Djava.ext.dirs指定目录下的jar包的导入。
系统类加载器/系统类加载器
主要负责Java-class path//-DJ ava.class.path指向的目录下的类和jar的包装工作。
User Custom ClassLoader/用户定义的类加载器(java.lang.ClassLoader的子类) ) ) ) )。
在程序运行时,通过java.lang.ClassLoader的子类动态加载class文件,以表现java动态实时类读取特性。
类加载器特性:
每个ClassLoader都有自己的命名空间,不能在同一命名空间中出现两个同名的类。
为了实现java安全沙箱模型的顶层类加载器安全机制,java默认采用“由父母委托的加载链”结构。
自定义类加载器加载类的步骤
类加载器-加载类
ClassLoader类加载的逻辑分析。 以下逻辑是加载类加载器的过程,但BootstrapClassLoader除外。
//检查是否加载了类
classc=findloadedclass(name;
if(c==null ) {
//未加载指定的类
try{
if(Parent!=空) {
//如果父类加载器不为空,则将加载委托给父类
c=parent.loadclass(name,false );
}else{
//如果父类加载器为空,则将加载委托给启动类
c=findbootstrapclass0(name;
}
}catch(classnotfoundexceptione ) )
//启动类加载器或父类加载器抛出异常时,当前类加载器将其
//捕获并使用findClass方法自行加载
c=查找类(name );
}
}
线程上下文类加载器
java的默认线程上下文类加载器是“系统类加载器”(AppClassLoader )。
//nowcreatetheclassloadertousetolaunchtheapplication
try{
loader=app class loader.getappclassloader (extcl;
}catch(ioexceptione ) {
thrownewInternalError (
' Could not create applicationclass '
loader" );}
// Also set the context class loader for the primordial thread.
Thread.currentThread().setContextClassLoader(loader);
以上代码摘自sun.misc.Launch的无参构造函数Launch()。
使用线程上下文类加载器, 可以在执行线程中, 抛弃双亲委派加载链模式, 使用线程上下文里的类加载器加载类.
典型的例子有, 通过线程上下文来加载第三方库jndi实现, 而不依赖于双亲委派.
大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
还有一些采用 hotswap 特性的框架, 也使用了线程上下文类加载器, 比如 seasar (full stack framework in japenese).
线程上下文从根本解决了一般应用不能违背双亲委派模式的问题.
使java类加载体系显得更灵活.
随着多核时代的来临, 相信多线程开发将会越来越多地进入程序员的实际编码过程中. 因此,
在编写基础设施时, 通过使用线程上下文来加载类, 应该是一个很好的选择。
当然, 好东西都有利弊. 使用线程上下文加载类, 也要注意, 保证多根需要通信的线程间的类加载器应该是同一个,
防止因为不同的类加载器, 导致类型转换异常(ClassCastException)。
三.命名空间及其作用每个类装载器有自己的命名空间,命名空间由所有以此装载器为创始类装载器的类组成。不同命名空间的两个类是不可见的,但只要得到类所对应的Class对象的reference,还是可以访问另一命名空间的类。
例2演示了一个命名空间的类如何使用另一命名空间的类。在例子中,LoaderSample2由系统类装载器装载,LoaderSample3由自定义的装载器loader负责装载,两个类不在同一命名空间,但LoaderSample2得到了LoaderSample3所对应的Class对象的reference,所以它可以访问LoaderSampl3中公共的成员(如age)。
例2不同命名空间的类的访问
/*LoaderSample2.java*/
import java.net. * ;
import java.lang.reflect. * ;
public class LoaderSample2 {
public static void main(String[] args) {
try {
String path = System.getProperty( " user.dir " );
URL[] us = { new URL( " file:// " + path + " /sub/ " )};
ClassLoader loader = new URLClassLoader(us);
Class c = loader.loadClass( " LoaderSample3 " );
Object o = c.newInstance();
Field f = c.getField( " age " );
int age = f.getInt(o);
System.out.println( " age is " + age);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*sub/Loadersample3.java*/
public class LoaderSample3 {
static {
System.out.println( " LoaderSample3 loaded " );
}
public int age = 30 ;
}
编译:javac LoaderSample2.java; javac sub/LoaderSample3.java
运行:java LoaderSample2
LoaderSample3 loaded
age is 30
从运行结果中可以看出,在类LoaderSample2中可以创建处于另一命名空间的类LoaderSample3中的对象并可以访问其公共成员age。
说明:如果LoaderSample3在classpath下能够找到,则由URLClassLoader的parent loader AppClassLoader来加载,如果不在classpath下
则由URLClassLoader自己加载,即LoaderSample3.getClass().getClassLoader() 是URLClassLoader
运行时包(runtime package)
由同一类装载器定义装载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看的定义类装载器是否相同。只有属于同一运行时包的类才能互相访问包可见的类和成员。这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见成员的情况。假设用户自己定义了一个类java.lang.Yes,并用用户自定义的类装载器装载,由于java.lang.Yes和核心类库java.lang.*由不同的装载器装载,它们属于不同的运行时包,所以java.lang.Yes不能访问核心类库java.lang中类的包可见的成员。
总结命名空间并没有完全禁止属于不同空间的类的互相访问,双亲委托模型加强了Java的安全,运行时包增加了对包可见成员的保护。二. 扩展ClassLoader方法我们目的是从本地文件系统使用我们实现的类装载器装载一个类。为了创建自己的类装载器我们应该扩展ClassLoader类,这是一个抽象类。我们创建一个FileClassLoader extends ClassLoader。我们需要覆盖ClassLoader中的findClass(String name)方法,这个方法通过类的名字而得到一个Class对象。
public Class findClass(String name)
{
byte [] data = loadClassData(name);
return defineClass(name, data, 0 , data.length);
}
我们还应该提供一个方法loadClassData(String name),通过类的名称返回class文件的字 节数组。然后使用ClassLoader提供的defineClass()方法我们就可以返回Class对象了。
public byte [] loadClassData(String name)
{
FileInputStream fis = null ;
byte [] data = null ;
try
{
fis = new FileInputStream( new File(drive + name + fileType));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int ch = 0 ;
while ((ch = fis.read()) != - 1 )
{
baos.write(ch);
}
data = baos.toByteArray();
} catch (IOException e)
{
e.printStackTrace();
}
return data;
}