乐趣区

关于java:JVM元数据区

如果你感觉该文章不错,请各位读者棘手点个赞,如果想要学些更多对于 JVM 的常识,请微信搜寻公众号 shysh95

元数据区

元数据区的概念呈现在 Java8 当前,在 Java8 以前成为办法区,元数据区也是一块线程共享的内存区域,次要用来保留被虚拟机加载的 类信息、常量、动态变量以及即时编译器编译后的代码等数据。

因为元数据存储的信息不容易变动,因而它被安置在一块堆外内存,大小由 -XX:MaxMetaspaceSize 指定。

public class MetaSpaceTest {public static void main(String[] args) {
        int i = 0;
        try {for (i = 0; i < 100000; i++) {new CglibBean(new HashMap<>());
            }
        } catch (Exception e) {System.out.println(e.getMessage());
        } finally {System.out.println("total create count:" + i);
        }
    }

    public static class CglibBean {public CglibBean(Object object) {Enhancer enhancer = new Enhancer();
            enhancer.setUseCache(false);
            enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> obj);
            enhancer.setSuperclass(object.getClass());
            enhancer.create();}
    }
}

上述代码通过 Cglib 生成大量的 HashMap 代理,上面咱们在运行这段代码的时候指定下列参数

-XX:MaxMetaspaceSize=100M -XX:+PrintGCDetails

当咱们程序循环至 3660 次,也就是说咱们大概在生成了约 3660 个代理类当前元数据区产生了内存溢出,上面将 MaxMetaspaceSize 改为 50M 执行,

从上图能够看出当咱们生成了 1710 个代理类当前元数据区产生了内存溢出,可见 一个元数据区的大小决定了 Java 虚拟机能够装载的类的多少。

运行时常量池

在元数据区中还有一块区域称为运行时常量池,此区域用来程序运行期间产生的常量,以及编译期生成的各种字面量和符号援用经类加载后的内容。

在 Java 中大略存储三种常量池概念,上面咱们来讲一下 Java 中其余两种常量池,帮忙读者理解他们中的区别。

首先大家在了解常量池的时候不要简略的了解为被 final 润饰的变量,常量在这里的含意是所有不变的货色,包含 final 润饰的变量、字面量、类和接口全限定名、字段、办法名称以及修饰符等永恒不变的货色

类文件常量池

类文件常量池是指.class 文件中的 Constant_Pool 项,如下图,类文件常量池寄存的都是一些字面量和符号援用。

并不是所有的字面量都会存储在类文件常量池中,比方对于办法内(留神是办法)整数字面量,如果值在 -32768~32767 之间则会被间接嵌入 JVM 指令中去,不会保留在常量池中。
所以读者不会在常量池中晓得 CONSTANT_Integer_info 为 1 的符号援用。

类文件常量池产生于编译期间,当 JVM 加载类文件时会将类文件常量池中的合乎援用替换间接援用,加载之后的类文件信息将会被寄存在运行时常量池。

字符串池

字符串池存在 JDK1.6 以前是寄存在永恒区中,然而在 JDK1.7 当前就被转移到堆上。

public static void intern() {String s1 = new String("he") + new String("llo");
    String s2 = s1.intern();
    System.out.println(s1 == s2);
}

上述代码在 JDK1.6 的时候将会创立 6 个对象,首先 new String(“he”)会在堆上创一个对象,并且 ”he” 字面量会在永恒区的字符串池上创立一个对象,new String(“llo”)同理创立了两个对象,最初的 + 又创立了一个对象,当调用 intern()办法时,首先会去查找字符串池查找是否有 hello 内容的对象,发现没有则会在永恒区中再创立一个对象,因而总共有 6 个对象,因为 s1 是堆中的对象,s2 是永恒区字符串池中的对象,因而 s1==s2 后果为 false,详情如下图

然而在 JDK1.6 当前成果不再如此,起因就是因为字符串常量池被移到了堆中,intern 办法也做了优化,在 JDK1.6 当前上述代码将会创立 5 个对象,首先 new String(“he”)会在堆上创一个对象,并且 ”he” 字面量也会在堆上创立一个对象, new String(“llo”)同理创立了两个对象,最初的 + 又创立了一个对象。当 intern 调用时,首先会在字符串池中查找是否有 hello 内容的对象,发现没有,此时不会被动创立而是先去查找堆中是否有 hello 内容的对象,如果有则间接将指针指向堆中的示例,因而这里一共会创立 5 个对象,因为 s1 和 s2 指向的是同一个对象实例,因而 s1==s2 为 true,详情如下图

问题

为了帮忙各位读者真正了解字符串常量池,上面有两段代码,请在脑海中给出后果,而后再进行 Coding 验证

public static void intern() {String s1 = new String("he") + new String("llo");
    String s2 = s1.intern();
    String s3 = "hello";
    System.out.println(s1 == s3);
    System.out.println(s1 == s2);
}
public static void intern() {
    String s3 = "hello";
    String s1 = new String("he") + new String("llo");
    String s2 = s1.intern();
    System.out.println(s1 == s3);
    System.out.println(s1 == s2);
}
退出移动版