JVM元数据区

以前咱们已经讲述过Java栈和堆,今天咱们看一下JVM另外一块重要的内存区域,那就是元数据区。读者若是对JVM的其余文章感兴趣的话能够经过上方专辑进入查看JVM系列文章

元数据区

元数据区的概念出如今Java8之后,在Java8之前成为方法区,元数据区也是一块线程共享的内存区域,主要用来保存被虚拟机加载的类信息、常量、静态变量以及即时编译器编译后的代码等数据。java

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

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代理,下面咱们在运行这段代码的时候指定下列参数shell

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


当咱们程序循环至3660次,也就是说咱们大约在生成了约3660个代理类之后元数据区发生了内存溢出,下面将MaxMetaspaceSize改成50M执行,微信


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

运行时常量池

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

在Java中大概存储三种常量池概念,下面咱们来说一下Java中其余两种常量池,帮助读者了解他们中的区别。优化

首先你们在理解常量池的时候不要简单的理解为被final修饰的变量,常量在这里的含义是一切不变的东西,包括final修饰的变量、字面量、类和接口全限定名、字段、方法名称以及修饰符等永恒不变的东西spa

类文件常量池

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

并非全部的字面量都会存储在类文件常量池中,好比对于方法内(注意是方法)整数字面量,若是值在-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);}


本文分享自微信公众号 - shysh95(shysh95)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索