类的命名空间与卸载详解及jvisualvm使用
类的命名空间详解:
在上一次【https://www.cnblogs.com/webor2006/p/9108301.html】最后实验中有一个违背咱们理解的,这里回顾一下:
也就是说,"某一个类对应的class文件只能被加载一次",这是有一个大前提的,该前提就是"该类加载器必须是在同一个命名空间里",如果在不同的命名空间同一个类的字节码文件是可以被加载多次的,那带着命名空间的概念咱们再来解析一下上面的输出:
在要加载的MyTest1字节码文件从编译工程中删除之后,由于都会由咱们自己所编写的自定义类加载器MyTest16所加载,而由于是两个不同类加载器,虽说类加载器的类型都属于自定义的MyTest16,这里可以增加一个日志来直观的体会一下:
编译运行:
那如果不将工程中编译的MyTest1字节码文件删掉其运行结果又是:
搞清楚了命名空间概念之后,下面继续基于该程序进行改造,前提还是MyTest1的字节码文件已经从工程中被删掉了,改造如下:
那结果是什么呢?
其实这个细节在loadClass()方法中已经有说明:
另外还需要注意一个细节,这里先回顾一下类加载器的层次体系:
虽说图中表达的是一种树形的结构,其实是一种包含关系,子加载器包含父加载器的一个引用,其实这种引用关系还可以进一步延伸,以咱们的这个例子为例:
下面再来定义一个类加载器:
重新编译,此时MyTest1的字节码文件已经在工程当中了,运行:
那接下来将MyTest1的字节码文件从工程中删掉,那这次结果又会有啥变化呢?
好,继续再修改:
编译运行,此时MyTest1的字节码文件存于工程当中了:
那如果将MyTest1的字节码文件给删掉其结果又会如何呢?
以上通过这么详细的实验是不是对类的双亲委托以及命名空间的概念有了非常深刻的认识。
类的卸载详解与jvisualvm使用:
- 当MySample类被加载、连接和初始化后,它的生命周期就开始了。当代表MySample类的Class对象不再被引用,既不可触及时,Class对象就会结束生命周期,MySample类在方法区内的数据也会被卸载,从而结束Sample类的生命周期。
- 一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期。
- 由Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。前面已经介绍过,Java虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。Java虚拟机本身会始终引用这些类加载器。而这些类加载器则会始终引用它们所加载类的类的Class对象,因此这些Class对象始终是可触及的。【言外之意就是说三大系统类加载器加载的Class对象是无法被卸载的】
- 由用户自定义的类加载器所加载的类是可以被卸载的。【如咱们自定义的MyTest16加载器所加载的类】
- 运行以上程序时,Sample类由loader1加载。在类加载器的内部实现中,用一个Java集合来存放所加载类的引用。另一方面,一个Class对象总是会引用它的类加载器。调用Class对象的getClassLoader()方法,就能获取它的类加载器,由此可见,代表Sample类的Class实例与loader1之间为双向关联关系。一个类的实例总是引用代表这个类的Class对象,在Object类中定义了getClass()方法,这个方法返回代表对象所属类的Class对象的引用。此外,所有的Java类都有一个静态属性class,它引用代表这个类的Class对象。
上面一大堆的理论有木有实验能清晰的看到类被卸载的呢?下面就来实验一下:
那如何能看到类的卸载信息呢?这时就需要加一个JVM的运行参数"-XX:+TraceClassUnloading",如下:
再次运行:
那如何来模拟呢,看下面:
那:
试试不加:
那如果就想不加那三断置null的语句也能看到类被卸载的信息,如何搞呢?
其实还可以利用jvisualvm小工具也能查看到类被卸载的记录,代码还是先还原,如下:
先运行jvisualvm:
然后再启动咱们的测试程序:
此时进程信息就会出现在jvisualvm工具之上,如下:
然后选择它打开具体的监视信息:
关于jvisualvm的其它功能待以后学习慢慢挖掘~