Java中方法区的相关问题

69 阅读3分钟

1.方法区概述

现给出一行代码,看出堆、栈、方法区的关系。

Person p = new Person(10,"sss");

其中p存放在栈空间中,Person存放在方法区中,new Person(10,"sss")存放在堆空间中。

在前面关于堆空间的概述中,写了堆空间分成三部分。年轻代、老年代、永久代。但是在实际的虚拟机规范中,还是尝试把永久代或者称为方法区分开,这一部分称为非堆部分。原因是,这一部分空间在一些JVM中不会涉及到垃圾回收。这个地方呢主要是存储类的一些加载信息。当加载的类过多的时候,就会报OOM异常。

现在说一下方法区,永久代,元空间的关系。方法区可以认为是一个接口,永久代和元空间可以看作是方法区的不同实现方式。jdk7之前,采用永久代的实现方式;jdk8及以后采用的是元空间的实现方式。这两种实现方式上最大的区别就是元空间所占用的是本地内存,而永久代占用的是JVM虚拟机的内存。因此,元空间可以允许加载的类数量会更多。

2.方法区的内存结构

常量池

常量池是存在于字节码文件即.class文件的一个特殊属性。在这个常量池中存在了.java代码的各种字面量,例如“abc”,20等等这些显式表示的值,同时包含了代码中的各种符号引用,例如java/lang/StringBuilder。这种完全是由字母构成的东西。
为什么要有这些东西呢:原因如下,在一个很简单的代码中,例如仅仅包含了一个main函数,内部仅仅是System.out.println("a")。其实这个代码也加载了非常多的类等其他信息,总之背后包含了特别多的东西。因此,如果把这些东西都打包进.class文件那么这个文件会非常的大,非常冗余,因此为了保持这些必要的信息,同时压缩.class文件的大小,只在.class文件中保存有这些事物的字面量、符号引用。

2.1 运行时常量池

当.class文件加载到JVM虚拟机以后,常量池的这些东西,就会转化成实际的地址,可以执行具体的功能。具体来说就是“味精”变成了真正可以调味的味精。转换成了真正要用的方法等东西。此外这个运行时常量池的方法具备动态性,根据不同的情况下相同的函数会执行不同的逻辑例如前面讲的intern()方法

2.2类信息

  • 类型信息:包名,类名,访问修饰符,父类信息,接口信息
  • 属性信息:属性名,访问修饰符,类型
  • 方法信息:方法名,形参信息,访问修饰符,返回值信息,
  • JIT缓存