精华内容
下载资源
问答
  • 之前我写了篇博客: java自定义类加载器实现类隔离 ,里面介绍了如何自定义类加载器实现了类隔离。 通常情况下,在JSP,OSGI及其他一些支持热替换的库,都是需要进行类的卸载回收的,否则类在替换后,老的类就没用了...

    欢迎关注本人公众号

    在这里插入图片描述

    概述

    之前我写了篇博客: java自定义类加载器实现类隔离 ,里面介绍了如何自定义类加载器实现了类隔离。

    通常情况下,在JSP,OSGI及其他一些支持热替换的库,都是需要进行类的卸载回收的,否则类在替换后,老的类就没用了但是还在内存中,就会造成内存泄漏。

    我们知道类的卸载需要满足以下三个条件:

    • 该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。
    • 加载该类的ClassLoader已经被GC。
    • 该类的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法。

    所以在自定义类加载器时,就要注意这一点,如果你是希望其使用完成后就被卸载,那么就需要特别留意类加载器及类的作用域了。

    代码实现

    我这里还是在 java自定义类加载器实现类隔离 的基础上,对代码进行一些调整,使其可以在使用完成后可以被卸载。注意这里的代码只是学习使用的实例,如果用于生产环境还需要慎重。

    在D盘的a,b两个目录下准备Hello.class 和 Dog.class 两个文件还是使用 java自定义类加载器实现类隔离 提到的方法来实现。

    接下来对通过URLClassLoader实现的test0方法进行改造:

    @Test
    public void test0() throws Exception {
        test4();
    
        System.gc();
    
        TimeUnit.SECONDS.sleep(5);
    }
    
    public void test4() throws Exception {
        System.out.println(this.getClass().getClassLoader());
    
        URLClassLoader diskLoader = new URLClassLoader(new URL[]{new URL("file:/D:/liubenlong/a/")});//最后面的斜杠需要添加
        URLClassLoader diskLoader1 = new URLClassLoader(new URL[]{new URL("file:/D:/liubenlong/b/")});
    
        //加载class文件
        Class clz = diskLoader.loadClass("Hello");
        Constructor constructor = clz.getConstructor(String.class);
        Object obj = constructor.newInstance("tom");
    
        /**
         * 类Hello引用了类Dog,类加载器会主动加载被引用的类。
         * 注意一般是我们使用 URLClassLoader 实现自定义的类加载器。如果使用classLoader,则需要重写findClass方法来实现类字节码的加载
         */
        Method method = clz.getMethod("sayHello", null);
        //通过反射调用Test类的say方法
        method.invoke(obj, null);
    
        Class clz1 = diskLoader1.loadClass("Hello");
        Constructor constructor1 = clz1.getConstructor(String.class);
        Object obj1 = constructor1.newInstance("cat");
    
        Method method1 = clz1.getMethod("sayHello", null);
        //通过反射调用Test类的say方法
        method1.invoke(obj1, null);
    }
    

    这里System.gc();是为了主动触发GC进行类卸载。后面的sleep只是为了等待程序执行完成,输出结果。

    然后需要添加启动参数-verbose:class来打印出类加载及类卸载的日志信息。

    运行test0,输出

    //省略部分日志
    [0.482s][info][class,load] Hello source: file:/D:/liubenlong/a/
    [0.483s][info][class,load] Dog source: file:/D:/liubenlong/a/
    [0.484s][info][class,load] java.lang.invoke.StringConcatFactory source: jrt:/java.base
    //省略部分日志
    [0.508s][info][class,load] java.io.DataInputStream source: jrt:/java.base
    a hi ...  java.net.URLClassLoader@3891771e
    //省略部分日志
    [0.549s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000100101c40 source: java.lang.invoke.LambdaForm
    a hello tom  java.net.URLClassLoader@3891771e
    [0.552s][info][class,load] Hello source: file:/D:/liubenlong/b/
    [0.556s][info][class,load] Dog source: file:/D:/liubenlong/b/
    b hi ...  java.net.URLClassLoader@78ac1102
    b hello cat  java.net.URLClassLoader@78ac1102
    [0.560s][info][class,unload] unloading class Dog 0x0000000100102258
    [0.560s][info][class,unload] unloading class Hello 0x0000000100102040
    [0.560s][info][class,unload] unloading class Dog 0x00000001000a1a58
    [0.560s][info][class,unload] unloading class Hello 0x00000001000a1840
    [5.581s][info][class,load  ] java.lang.Shutdown source: jrt:/java.base
    [5.581s][info][class,load  ] java.lang.Shutdown$Lock source: jrt:/java.base
    
    Process finished with exit code 0
    

    日志中可以看出class load 和 unload 的信息,以及Hello和Dog类的类加载器。

    注意,我这里时是加了一个test4方法来进行操作类及处理业务逻辑,test0调用test4方法并且调用System.gc();。如果System.gc();直接写道test4方法的末尾,是无法实现类的卸载的。读者可以自己实验。

    原因是如果放在一起的话,都是同一个方法内,方法是虚拟机执行的最小单元,调用test4方法时会生成一个栈帧放到栈顶。test4方法的局部变量就会存在与栈帧中的局部变量表中,这里就有URLClassLoader类加载器及Hello/Dog类实例的引用,还包括一些动态链接,所以在GC时,由于栈帧中的内容是作为GC ROOT的,所以肯定不会被回收,故而不会进行类的卸载。
    注意在实际开发中一定要保证类的实例, 该类的ClassLoader都被回收,并且没该类不可以被反射调用,才可以类卸载。

    展开全文
  • 类的卸载:由JVM自带的类加载器所加载...由用户自定义的类加载器所加载的类是可以被卸载的。对于每一个Class对象,可以通过其getClassLoader()方法获得其类加载器的引用,所以Class对象内部有指向其类加载器的引用;...

    类的卸载:由JVM自带的类加载器所加载的类,在JVM的生命周期中,始终不会被卸载。JVM本身会始终引用这些类加载器,而这些类加载器始终引用它们所加载的类的Class对象。所以说,这些Class对象始终是可触及的。

    由用户自定义的类加载器所加载的类是可以被卸载的。

    对于每一个Class对象,可以通过其getClassLoader()方法获得其类加载器的引用,所以Class对象内部有指向其类加载器的引用;而对于ClassLoader对象,在内部实现中,用一个Java集合来存放所已经加载过的类的引用。所以对于Class对象和ClassLoader对象之间是双向关联的。

    一个类的实例总是引用代表这个类的Class对象,在Object类中定义了getClass()方法,这个方法返回该对象所代表类的Class对象的引用。此外,所有的Java类都有一个静态属性class,它引用代表这个类的Class对象。

    Class objClass = loader1.loadClass("Sample");

    System.out.println(objClass.hashCode());

    Object obj = objClass.newInstance();

    //5

    loader1 = null;

    objClass = null;

    obj = null;

    //8

    loader1 = new MyClassLoader("loader1");

    loader1.setPath("D:\\myapp\\serverlib\\");

    objClass = loader1.loadClass("Sample");

    System.out.println(objClass.hashCode());

    obj = objClass.newInstance();

    //10

    输出结果:

    f892b3114da907acc4e9cea9950db3b3.png

    下面我们来分析:

    01268d7600287dd7e7ac3ce508c6f517.png

    其中,loader1指向MyClassLoader对象

    objClass指向代表Sample类的Class对象

    obj指向Sample对象

    类加载器与被该类加载器加载的Class对象之间相互关联,Sample对象指向代表Sample类的Class对象。其中,Class对象又指向方法区中Sample类的二进制数据结构。

    当执行到8处时,三个引用变量都置为null,此时MyClassLoader对象结束生命周期,Sample类也会结束生命周期,代表Sample类的Class对象结束生命周期,Sample类在方法去的二进制数据结构也会被卸载。

    当执行到10处时,都重新被创建加载,此时结构又如上图所示,不过这两次加载的Class对象并不是同一个,我们从输出结果可以看出hashCode值并不同。

    如果同时对某一个类加载两次,如下:

    Class clazz = loader1.loadClass("Sample");

    Class clazz2 = loader1.loadClass("Sample");

    System.out.println(clazz == clazz2);

    输出结果为:true 同一个类的Class对象只有一个,当该类对象被加载后,就不会再去加载该类对应的Class对象,即使又执行了加载对象的操作。

    展开全文
  • 前言 学习类加载器就一定要自己实现一个类加载器,今天就从一个简单的自定义类加载器说起。...import java.io.*; public class CustomizedClassLoader extends ClassLoader { private String classLoaderName;

    前言

    学习类加载器就一定要自己实现一个类加载器,今天就从一个简单的自定义类加载器说起。

    自定义类加载器

    例1

    一个简单的类加载器,从一个给定的二进制名字读取一个字节码文件的内容,然后生成对应的class对象。

    package com.jamie.jvmstudy;
    
    import java.io.*;
    
    public class CustomizedClassLoader extends ClassLoader {
    
        private String classLoaderName;
    
        private String fileExtension = ".class";
    
        public CustomizedClassLoader(String classLoaderName) {
            super(); //如果调用默认构造器,代表默认的父类加载器是系统类加载器SystemClassLoader
            this.classLoaderName = classLoaderName;
        }
    
        public CustomizedClassLoader(ClassLoader parent, String classLoaderName) {
            super(parent); //如果指定父类加载器,那么该构造器执行完之后,这个类加载器就有指定的parent了。(默认是系统类加载器)
            this.classLoaderName = classLoaderName;
        }
    
        @Override
        public Class<?> findClass(String className) throws ClassNotFoundException {
            byte[] data = this.loadClassData(className);
    
            return this.defineClass(className, data, 0, data.length);
        }
    
        private byte[] loadClassData(String className) {
            byte[] data = null;
            try(InputStream is = new FileInputStream(new File(className + this.fileExtension));
                ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                int ch;
                while(-1 != (ch = is.read())) {
                    baos.write(ch);
                }
                data = baos.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return data;
        }
    
        public static void main(String[] args) throws Exception {
            CustomizedClassLoader customizedClassLoader = new CustomizedClassLoader("jamie loader");
            test(customizedClassLoader);
        }
    
        private static void test(ClassLoader classLoader) throws Exception {
            Class<?> myClass = classLoader.loadClass("com.jamie.jvmstudy.TestClassLoader");
            Object o = myClass.newInstance();
            System.out.println(String.format("classLoader in this method is [%s]", classLoader));
            System.out.println(String.format("object [%s] has been created by [%s].", o, myClass.getClassLoader()));
            System.out.println(String.format("class loader of CustomizedClassLoader is [%s]", classLoader.getClass().getClassLoader()));
        }
    }
    

    运行结果如下:

    classLoader in this method is [com.jamie.jvmstudy.CustomizedClassLoader@4b67cf4d]
    object [com.jamie.jvmstudy.TestClassLoader@7ea987ac] has been created by [sun.misc.Launcher$AppClassLoader@14dad5dc].
    class loader of CustomizedClassLoader is [sun.misc.Launcher$AppClassLoader@14dad5dc]
    

    这里重点说明一下:示例中的"com.jamie.jvmstudy.TestClassLoader"Class是被系统类加载器加载的,而不是我们自定义的加载器。
    原因:

    1. 自定义的加载器CustomizedClassLoader的父类构造器是系统类加载器。因为我们加载类调用的方法是:classLoader.loadClass("com.jamie.jvmstudy.TestClassLoader")
    2. 默认的loadClass()方法实现就是双亲委派的源码实现,因为系统类加载器会在当前的classpath(类路径)下查找是否存在匹配的"binary name",如果存在,则系统类加载器加载成功。
    3. 所以只要类路径下存在匹配的二进制名字的字节码,就会被系统类加载器成功加载。

    例2

    基于例1的基础上,自定义加载器获取字节码文件的内容改为从一个指定的路径中读取。并且传入一个非classpath的路径,去加载某个字节码。
    因为自定义的类加载器默认的父类加载器是系统类加载器,运行下例的时候,需要在编译之后把类路径下的com.jamie.jvmstudy.TestClassLoader字节码文件删除(防止[双亲委托机制]使[系统类加载器AppClassLoader]把指定类加载进虚拟机),然后运行本例。

    package com.jamie.jvmstudy;
    
    import java.io.*;
    
    public class CustomizedClassLoader extends ClassLoader {
    
        private String classLoaderName;
    
        private String path;
    
        private String fileExtension = ".class";
    
        public CustomizedClassLoader(String classLoaderName) {
            super(); //如果调用默认构造器,代表默认的父类加载器是系统类加载器SystemClassLoader
            this.classLoaderName = classLoaderName;
        }
    
        public CustomizedClassLoader(ClassLoader parent, String classLoaderName) {
            super(parent);
            this.classLoaderName = classLoaderName;
        }
    
        @Override
        public Class<?> findClass(String className) throws ClassNotFoundException {
            System.out.println("Self findClass() invoked");
            byte[] data = this.loadClassData(className);
            return this.defineClass(className, data, 0, data.length);
        }
    
        private byte[] loadClassData(String className) {
            byte[] data = null;
            className = className.replace(".", "/");
            try(InputStream is = new FileInputStream(new File(path + className + this.fileExtension));
                ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                int ch;
                while(-1 != (ch = is.read())) {
                    baos.write(ch);
                }
                data = baos.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return data;
        }
    
        public static void main(String[] args) throws Exception {
            CustomizedClassLoader loader1 = new CustomizedClassLoader("jamie loader1");
            loader1.setPath("D:/temp/");
            Class<?> myClass1 = loader1.loadClass("com.jamie.jvmstudy.TestClassLoader");
            System.out.println(String.format("Hashcode of myClass1 is [%s].", myClass1.hashCode()));
            System.out.println(String.format("myClass1 is [%s]", myClass1));
            System.out.println();
    
            CustomizedClassLoader loader2 = new CustomizedClassLoader("jamie loader2");
            loader2.setPath("D:/temp/");
            Class<?> myClass2 = loader2.loadClass("com.jamie.jvmstudy.TestClassLoader");
            System.out.println(String.format("Hashcode of myClass2 is [%s].", myClass2.hashCode()));
            System.out.println(String.format("myClass2 is [%s]", myClass2));
    
            System.out.println();
            System.out.println("myClass1 == myClass2 ? " + (myClass1 == myClass2));
        }
    
        public void setPath(String path) {
            this.path = path;
        }
    }
    

    运行结果如下:

    Self findClass() invoked
    Hashcode of myClass1 is [1956725890].
    myClass1 is [class com.jamie.jvmstudy.TestClassLoader]
    
    Self findClass() invoked
    Hashcode of myClass2 is [21685669].
    myClass2 is [class com.jamie.jvmstudy.TestClassLoader]
    
    myClass1 == myClass2 ? false
    

    结论:由运行结果可以看出,此时的虚拟机中出现了两个不一样的TestClassLoader.class对象。
    这就引入了类加载器的命名空间的问题。
    我们之前理解的“Class对象只存在一份”是基于同一个类加载器的命名空间来说的。

    类加载器的命名空间

    • 每个类加载器都有自己的命名空间。命名空间由该加载器和所有父加载器所加载的类组成。
    • 在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类。
    • 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。

    后续写了一篇文章类加载器之命名空间详解,里面有对【命名空间】更加详细的分析与示例。感兴趣请跳转。

    类的卸载

    • 当一个类被加载、连接和初始化之后,它的生命周期就开始了。当代表该类的Class对象不再被引用,既不可达时,Class对象就会结束生命周期,该类在方法区内的数据也会被卸载,从而结束该类的生命周期。
    • 一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期。
    • 被Java虚拟机自带的ClassLoader加载的类,是不会被卸载的。因为JVM本身会始终引用这些ClassLoader,而这些ClassLoader始终会引用它加载的所有类的Class对象。所以它们永远可达。
    • 只有自定义的类加载器加载的类,才有可能被卸载。

    类卸载的证明:

    基于上述例2,改造一下main方法,并在执行时,添加JVM运行参数:-XX:+TraceClassUnloading

        public static void main(String[] args) throws Exception {
            CustomizedClassLoader loader1 = new CustomizedClassLoader("jamie loader1");
            loader1.setPath("D:/temp/");
            Class<?> myClass1 = loader1.loadClass("com.jamie.jvmstudy.TestClassLoader");
            System.out.println(String.format("Hashcode of myClass1 is [%s].", myClass1.hashCode()));
            System.out.println(String.format("myClass1 is [%s]", myClass1));
            System.out.println();
    
            loader1 = null;
            myClass1 = null;
            System.gc();
    
            CustomizedClassLoader loader2 = new CustomizedClassLoader("jamie loader2");
            loader2.setPath("D:/temp/");
            Class<?> myClass2 = loader2.loadClass("com.jamie.jvmstudy.TestClassLoader");
            System.out.println(String.format("Hashcode of myClass2 is [%s].", myClass2.hashCode()));
            System.out.println(String.format("myClass2 is [%s]", myClass2));
    
            System.out.println();
            System.out.println("myClass1 == myClass2 ? " + (myClass1 == myClass2));
            Thread.sleep(50000); //为了查看类的卸载情况增加延时
        }
    

    运行结果:可以看到TestClassLoader类被卸载一次。

    =====Self findClass() invoked=====
    Hashcode of myClass1 is [1956725890].
    myClass1 is [class com.jamie.jvmstudy.TestClassLoader]
    
    [Unloading class com.jamie.jvmstudy.TestClassLoader 0x00000007c0061028]
    =====Self findClass() invoked=====
    Hashcode of myClass2 is [21685669].
    myClass2 is [class com.jamie.jvmstudy.TestClassLoader]
    
    myClass1 == myClass2 ? false
    

    增加main方法睡眠时间,使用Java VisualVM可以看到下图:

    分类

    展开全文
  • //先写一个类,里面含有static代码块的public class Goodest { static { System.out.println("我只是一个人才");...import java.io.InputStream; //自定义类加载器 class MyClassLoader extends ClassLoader {
    //先写一个类,里面含有static代码块的
    public class Goodest {
    
    static
    {
    System.out.println("我只是一个人才");<span style="white-space:pre">
    }
    
    }
    
    import java.io.InputStream;
    
     //自定义类加载器
     class MyClassLoader extends ClassLoader {
    	
    	@Override
    	public Class<?> loadClass(String name) throws ClassNotFoundException{
    		try {
    			String fileName = name.replace('.', '\\')+".class";
    			InputStream is = getClass().getResourceAsStream(fileName);
    			if (null == is) {
    				return super.loadClass(name);
    			}
    
    			byte[] b = new byte[is.available()];
    			is.read(b);
    			return defineClass(name, b, 0, b.length);
    
    		} catch (Exception ex) {
    			ex.printStackTrace();
    			throw new ClassNotFoundException(name);
    		}
    	}
    
    	@Override
    	protected void finalize() throws Throwable {
    		System.out.println("我被抛弃了!");
    	}
    
    }
    
    
    public class Doit {   
    	
    	public static void main(String[] args){
    		
    		//是有自定义类加载器两次加载,static两次被调用,类加载器无引用后被回收
    		MyClassLoader my = new MyClassLoader();
    		try {
    			Class<?> clazz =my.loadClass("Goodest");
    			System.out.println("Class' HashCode! ="+clazz.hashCode());
    			clazz.newInstance();
    			clazz = null;
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		System.gc();
    		
    		try {
    			Thread.sleep(10000L);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		
    		MyClassLoader your = new MyClassLoader();
    		try {
    			Class<?> clazz =your.loadClass("Goodest");
    			System.out.println("Class' HashCode! ="+clazz.hashCode());
    			clazz.newInstance();
    			clazz = null;
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		your = null;
    		System.gc();
    		
    		
    		System.out.println("完!");
    		
    	}
    
    	
    
    }
    


    展开全文
  • Java中类的卸载机制

    千次阅读 2018-01-31 17:57:01
    类的卸载:由JVM自带的类加载器所加载的类,在JVM的... 由用户自定义的类加载器所加载的类是可以被卸载的。 对于每一个Class对象,可以通过其getClassLoader()方法获得其类加载器的引用,所以Class对象内部有指向其类
  • 一般情况下,类的加载都是由系统自带的类加载器完成,且对于同一个全限定名的java类,只能被加载一次,而且无法被卸载。可以使用自定义的 ClassLoader 替换系统的加载器,创建一个新的 Class...
  • 一般情况下,类的加载都是由系统自带的类加载器完成,且对于同一个全限定名的java类,只能被加载一次,而且无法被卸载。可以使用自定义的 ClassLoader 替换系统的加载器,创建一个新的 ClassLoader,再用它加载 ...
  • 类的卸载

    2017-09-11 10:28:15
    Java虚拟机自带的类加载器所...Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象,因此这些Class对象始终是可触及的。由用户自定义的类加载器加载的类是可以被卸载的。
  • 一般情况下,类的加载都是由系统自带的类加载器完成,且对于同一个全限定名的java类,只能被加载一次,而且无法被卸载。可以使用自定义的 ClassLoader 替换系统的加载器,创建一个新的 ClassLoader,再用它加载 ...
  • 一般情况下,类的加载都是由系统自带的类加载器完成,且对于同一个全限定名的java类,只能被加载一次,而且无法被卸载。可以使用自定义的 ClassLoader 替换系统的加载器,创建一个新的 ClassLoader,再用它加载 ...
  • 类加载的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。...3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。 虚拟机设计团队把类加载阶段中“通过一...
  • java类从被加载到JVM到卸载出JVM,整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸载(Unloading)七个阶段。 其中验证、...
  • 类加载机制java类从被加载到JVM到卸载出JVM,整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸载(Unloading)七个阶段。...
  • 文章目录1 类加载器命名空间2 运行时包3 初始类加载器4 类的卸载5 测试: 自定义一个java.lang.String 1 类加载器命名空间 每一个类加载器实例都有各自的命名空间,命名空间是由该加载器及其所有父加载器所构成的,...
  • 类加载机制java类从被加载到JVM到卸载出JVM,整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸载(Unloading)七个阶段。...
  • 原标题:透过现象看本质:Java类动态加载和热替换作者:maijun来自:华为云开发者社区摘要:本文主要介绍类加载器、自定义类加载器及类的加载和卸载等内容,并举例介绍了Java类的热替换。最近,遇到了两个和Java类的...
  • 通过Java预定义和自定义的类加载器,在运行时从网络或其他地方加载一个二进制流作为程序代码的一部分 类的生命周期:加载(Loading)、连接(Linking)[验证(verification)、准备(Preparation)、解析...
  • 自定义一个类加载器

    2020-04-16 16:40:20
    1.预加载:虚拟机启动时加载的是JAVA_HOME/lib/下的rt.jar下的.class文件,这个jar包下的内容像java.lang、java.io、java.util,等都是我们日常非常常用的,因此随着虚拟机启动时加载 2.运行时加载:虚拟机在用到...
  • 文章目录①. 说说类加载分几步?②. 什么是类的加载(Loading)③. 链接(Linking)④. 初始化(Initialization)②. 类加载器的介绍①. 启动类加载器②. 扩展类加载器(Extension ... 按照Java虚拟机规范,从class文件
  • 玩转Java虚拟机(四)

    2020-03-11 17:49:46
    打卡学习JVM,第四天 本人学习过程中所整理的代码,源码地址 - 类的卸载 当一个类被加载、连接和初始化后,它的生命周期就开始了。...由用户自定义的类加载器所加载的类是可以被卸载的,Java虚...
  • 玩转Java虚拟机(五)

    2020-03-13 22:07:58
    打卡学习JVM,第五天 本人学习过程中所整理的代码,源码地址 - 类的卸载 当一个类被加载、连接和初始化后,它的生命周期就开始了。...由用户自定义的类加载器所加载的类是可以被卸载的,Java虚...
  • 在《深入理解Java虚拟机》一说阐述为虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。类的加载机制属于类的生命周期的一部分,如下图: ...
  • 加载:就是把字节码文件从磁盘经过io流读取到内存里面的,使用的时候才会加载类,例如调用类的main()方法,new对象等等,在加载阶段会在内存生成一个代表这个类的java.lang.class的对象,作为方法区这个类的各种...
  • 深入Java虚拟机

    2014-06-28 23:53:10
    1.3.3 Java class文件 1.3.4 Java API 1.3.5 Java程序设计语言 1.4 Java体系结构的代价 1.5 结论 1.6 资源页 第2章 平台无关 2.1 为什么要平台无关 2.2 Java的体系结构对平台无关的支持 2.2.1...
  • 分配空间: 类变量是在准备阶段分配内存赋默认值 ... 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。 如果以上三个条件全部满足,jvm就会在方法区垃圾...
  • Java类加载机制

    2020-04-25 14:53:28
    简单来说,java类加载有以下几个过程: 加载->验证->准备->解析->初始化->使用->卸载 一、加载: · 通过类的全限定名获取其自定义二进制流 · 将二进制流所代表的静态数据结构转化为方法...
  • java监控.rar

    2019-05-16 08:54:15
    CCSC:压缩类空间大小(compressed class space) CCSU:压缩类空间已使用大小(KB) YGC:新生代gc次数 YGCT:新生代gc耗时(秒) FGC:Full gc次数 FGCT:Full gc耗时(秒) GCT:gc总耗时(秒) Loaded:...
  • Java热更新

    千次阅读 2016-05-08 15:31:01
    一、 解决方案 1) 自定义类加载器。 首先需要明白一点,class相等的判断条件不仅仅是类名相同,还...JVM内部class卸载的条件及其苛刻,甚至没有明确的方法可以直接调用,只有当加载该类型的类加载器实例为unreac
  • Java 类加载器详解

    2021-04-08 11:24:45
    解析:把类中的符号引用转换为直接引用初始化(类)使用卸载:结束生命周期类加载器JVM类加载机制类的初始化类加载方式JVM初始化步骤对象初始化方式参考资料对象的初始化对象初始化过程双亲委派模型自定义类加载器 ...
  • new对象时Java虚拟机的工作流程解析

    千次阅读 热门讨论 2021-04-11 10:41:51
    周日的上午,笔者像往常一样盯着电脑发呆,想起昨晚的梦中,一个身着西装的面试官问道:你知道new一个对象时,Java虚拟机是怎么工作的吗? 我: 没办法,那今天就复习一下好了。 类的生命周期分为7个阶段:加载、...

空空如也

空空如也

1 2 3 4
收藏数 69
精华内容 27
关键字:

自定义卸载classjava

java 订阅