6.方法区
6.1 方法区的基本理解:
方法区,是一个被线程共享的内存区域。其中主要存储加载的类字节码、 class/method/field 等元数据、static final 常量、static 变量、即时编译器编译后的代码等数据。另外,方法区包含了一个特殊的区域“运行时常量池”。
Java 虚拟机规范中明确说明:”尽管所有的方法区在逻辑上是属于堆的一部分,但对于 HotSpotJVM 而言,方法区还有一个别名叫做 Non-Heap(非堆),目的就是 要和堆分开.
所以,方法区看做是一块独立于 java 堆的内存空间.
方法区在 JVM 启动时被创建,并且它的实际的物理内存空间中和 Java 堆区一样都可以 是不连续的.
方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展.
方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出, 虚拟机同样会抛出内存溢出的错误
概述:(方法区也是一块内存空间,逻辑上属于堆,为了区分,称为元空间(jdk8以后),主要用于存储类的信息,在jvm启动时创建,大小可以分配,如果加载的类太多,也会报内存溢出的错误,是线程共享的)
public class Demo1 {
public static void main(String[] args) {
String temp = "world"; //字符串是常量
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String str = temp + temp;
temp = str;
str.intern();//将字符串存储到字符串常量池中 } }
}
}
}
运行结果:
方法区,栈,堆的交互关系:
6.2 方法区大小设置:
Java 方法区的大小不必是固定的,JVM 可以根据应用的需要动态调整.
元数据区大小可以使用参数-XX:MetaspaceSize 和 -XX:MaxMataspaceSize 指定,替代上述原有的两个参数.
默认值依赖于平台,windows 下,-XXMetaspaceSize是21MB,
-XX:MaxMetaspaceSize 的值是-1,没有限制.
这个-XX:MetaspaceSize 初始值是 21M 也称为高水位线,一旦触及就会触发 Full GC.
因此为了减少 FullGC 那么这个-XX:MetaspaceSize 可以设置一个较高的值.
概述:(方法区大小可以通过-XX:MetaspaceSize设置:
方法区在Windows中默认的大小是21Mb ,如果达到21Mb会触发FULL GC,因此可以将其值设置的大一些,会减少FUll GC的触发.)
6.3 方法区的内部结构:
方法区它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编 译后的代码缓存,运行常量池等。
通过反编译字节码文件查看.
反编译字节码文件,并输出值文本文件中,便于查看。参数 -p 确保能查看 private 权限类型的字段或方法
javap -v -p 文件名.class > test.txt
右击文件,选择 Open in Terminal
然后输入指令:
会生成一个txt文件:
打开后会看到编译后这个类的信息.
6.4 方法区的垃圾回收:
1.有些人认为方法区(如 Hotspot 虚拟机中的元空间或者永久代)是没有垃圾 收集行为的,其实不然。《Java 虚拟机规范》对方法区的约束是非常宽松的, 提到过可以不要求虚拟机在方法区中实现垃圾收集。
2.一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。但是这部分区域的回收有时又确实是必要的。
方法区的垃圾收集主要回收两部分内容:运行时常量池中废弃的常量和不再使用的类型。
下面也称作类卸载
判定一个常量是否“废弃”还是相对简单,而要判定一个类型是否属于“不再被 使用的类”的条件就比较苛刻了。需要同时满足下面三个条件:
1.该类所有的实例都已经被回收,也就是 Java 堆中不存在该类及其任何派生子类的实例。
2.加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加 载器的场景,如 OSGi、JSP 的重加载等,否则通常是很难达成的。
3.该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
概述:(方法区中主要回收运行时常量池,类的信息
类的信息卸载(回收)条件是比较苛刻的,满足的3个条件:
1.该类以及子类的对象没有被使用,
2.该类的类加载器被卸载,
3.该类的Class对象也没有被引用)