java 卸载class_Java Class卸载与ClassLoader

java 卸载class_Java Class卸载与ClassLoaderJVM中的Class只有满足以下三个条件,才能被GC回收,也就是该Class被卸载(unload):-该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。-加载该类的ClassLoader已经被GC。-该类的Java.lang.Class对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法jsp和java类是完全不一样的概念。jsp->servlet…

大家好,又见面了,我是你们的朋友全栈君。

JVM中的Class只有满足以下三个条件,才能被GC回收,也就是该Class被卸载(unload):

– 该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。

– 加载该类的ClassLoader已经被GC。

– 该类的Java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法

jsp和java类是完全不一样的概念。

jsp->servlet 在web容器中,你的servlet是单例的,也是无状态的,线程安全的。也就是只有一个对象,

jsp改变以后,web容器只要把相应的servlet对象更新就好了。

而java呢?

可能这个类在你的应用中有n个实例,与这些实例单向,双向关联的又有n个实例。如果你修改了,这些jvm存在的老的实例对象怎么办????

java这类静态语言无法实现象asp,php,jsp的效果的。

weblogic热部署原理

Weblogic允许在wls运行时部署组件的新版本。这个过程被称作热部署。因为Javaclassloader没有任何一种机制来卸下一系列存在的类,也不能用类的新版本来替换老版本,为了在一个运行的虚拟机中更新相关的类,classloader必须被替换掉。当它被替换时,它所装载的所有类以及衍生的子classloader也要被重新装载。这些类的所有实例也必需被重新装载。在wls中,每一个应用组件都有一个层次化的classloaders,它们都是system classloader的子类,这种结构有助于每个应用或应用的一部分能被单独重新加载,而不会影响其它的组件。

类加载器的种类:

Bootstrap ClassLoader/启动类加载器

主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作。

Extension ClassLoader/扩展类加载器

主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作。

System ClassLoader/系统类加载器

主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作。

User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)

在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。

类加载器的特性:

每个ClassLoader都维护了一份自己的名称空间, 同一个名称空间里不能出现两个同名的类。

为了实现java安全沙箱模型顶层的类加载器安全机制, java默认采用了 ” 双亲委派的加载链 ” 结构。

自定义类加载器加载一个类的步骤

classloader-load-class.jpg

classloader-load-class

ClassLoader 类加载逻辑分析, 以下逻辑是除 BootstrapClassLoader 外的类加载器加载流程:

// 检查类是否已被装载过

Class c = findLoadedClass(name);

if (c == null ) {

// 指定类未被装载过

try {

if (parent != null ) {

// 如果父类加载器不为空, 则委派给父类加载

c = parent.loadClass(name, false );

} else {

// 如果父类加载器为空, 则委派给启动类加载加载

c = findBootstrapClass0(name);

}

} catch (ClassNotFoundException e) {

// 启动类加载器或父类加载器抛出异常后, 当前类加载器将其

// 捕获, 并通过findClass方法, 由自身加载

c = findClass(name);

}

}

线程上下文类加载器

java默认的线程上下文类加载器是 系统类加载器(AppClassLoader)。

// Now create the class loader to use to launch the application

try {

loader = AppClassLoader.getAppClassLoader(extcl);

} catch (IOException e) {

throw new InternalError(

“Could not create application class 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;

}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/144573.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • PAT乙级题目答案汇总 PAT (Basic Level) Practice (中文)[通俗易懂]

    PAT乙级题目答案汇总 PAT (Basic Level) Practice (中文)[通俗易懂]题目列表:标号题目链接分数博客链接完成时间1001害死人不偿命的(3n+1)猜想151001害死人不偿命的(3n+1)猜想(15分)2020/8/01

    2022年5月29日
    38
  • MongoDB(三)——CRUD

    MongoDB(三)——CRUD

    2022年1月11日
    45
  • 一个概括性关于维数约简的论文

    一个概括性关于维数约简的论文
    http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.107.1327&rep=rep1&type=pdf

    2022年6月1日
    32
  • 基本字符串压缩

    基本字符串压缩题目 利用字符重复出现的次数 编写一个方法 实现基本的字符串压缩功能 比如 字符串 aabcccccaaa 经压缩会变成 a2b1c5a3 若压缩后的字符串没有变短 则返回原先的字符串 给定一个 stringiniStr 为待压缩的串 长度小于等于 3000 保证串内字符均由大小写英文字母组成 返回一个 string 为所求的压缩后或未变化的串 思路 咋一分析 题目还是很简单的

    2025年7月17日
    4
  • 暴力破解加密压缩文件

    暴力破解加密压缩文件实验目的应用Python程序设计语言的相关知识,对加密的压缩文件进行破解。实验所用仪器(或实验环境)Python3.8实验基本原理及步骤(或方案设计及理论计算)任务1:定义一个函数,函数带有一个参数L(类型为整数),函数返回一个长度为L、包含大小写字母和数字的随机密码(类型为字符串)。在任务1中,可以借助random中的choice,以及string库中的ascii_letters;至少用3个不同的长度测试函数的返回值。ascii_letters基本上是ascii_lowercase和

    2022年6月3日
    43
  • JVM 优化实战[通俗易懂]

    JVM 优化实战[通俗易懂]本文讲解了JVM的内存划分和分配策略,并以截图和脚本展示常用可视化和命令行工具的使用方法,完整演示了JVM优化、内存泄露排查、gc.log分析方法等。作者:王克锋 出处:https://kefeng.wang/2016/11/22/java-jvm/ 版权:自由转载-非商用-非衍生-保持署名,转载请标明作者和出处。1GC相关内存1.1内存划分1.1.1堆(Heap)存放 newM…

    2022年6月9日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号