0
点赞
收藏
分享

微信扫一扫

JVM系列之类的使用与卸载

前言

一个完整即将使用的类已经在JVM虚拟机中整装待发了,进击!!!

类的使用

类的使用一般可以分为主动使用和被动使用;

主动使用

  • 创建类的实例: new关键字、类工厂等
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射
  • 初始化一个类的子类
  • main方法
  • 动态语言支持(JDK1.7开始提供)

被动使用

除了以上7种外的所有调用,都是被动调用,都不会对类进行初始化,也就是只会进行类的加载、解析;

案例1

public class Test {
    public static void main(String[] args) {
①------System.out.println(Child.str);  
    }
}
class Parent{
    public static String str = "Parent";
    static {
        System.out.println("Parent 静态代码块");
    }
}
class Child extends Parent{
    public static String str2 = "Child ";
    static {
        System.out.println("Child 静态代码块");
    }
}

输出:

Parent 静态代码块
Parent

将①换成System.out.println(Child.str2)
输出:

Parent 静态代码块
Child 静态代码块
Child 

解析

  • 第一次输出虽然调用了Child.str,但实际上没有对Child进行主动使用,所以不会被初始化,不会输出静态代码块中的内容
  • 第二次输出时,Child是主动使用,Parent因为是Child的父类,所以也初始化,所以子类初始化,父类也会初始化

案例2

public class Test {
    public static void main(String[] args) {
        System.out.println(Parent.uuid);
    }
}
class Parent {
    public static final String uuid= UUID.randomUUID().toString();
    static {
        System.out.println("parent4 static block");
    }
}

解析

uuid是静态常量,但是在编译期uuid的值并不能确定,这个值就不会被放到常量池中,所以这个类的初始化会在被主动调用时触发。

总结

  • 类的初始化一定是首次、主动使用该类
  • 子类初始化,父类也会初始化,并且父类先初始化完毕(不适用于接口)
  • final修饰的常量,在编译阶段,就会被放在调用这个常量的方法的所在的类的常量池,调用类并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化;如果final定义的值随机产生或每次都会改变,则该值不会放入调用它的方法的常量池中,所以每次调用依旧触发该对象所在的类(主动使用),该类即被初始化
  • 当一个接口在初始化时,并不要求其父接口完成了初始化,只有在真正使用到父类接口的时候(如引用接口中定义的常量时),父类才会被初始化

常用java虚拟机参数(类加载/卸载跟踪)

-verbose:class 跟踪类的加载和卸载
-XX:+TraceClassLoading 跟踪类的加载
-XX:+TraceClassUnloading 跟踪类的卸载

-XX:+PrintClassHistogram 查看系统中类的分布情况
(需要在控制台按下Ctrl+Break组合键,就会出现柱状图,可以看到当前系统中占用空间最多的对象,以及其实例数量和空间大小 )
举报

相关推荐

0 条评论