运行时数据区-方法区
概括
- 方法区是在虚拟机启动时创建的
- 方法区是所有 Java 虚拟机线程之间共享的
- 它存储每个类的结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括在类和接口初始化以及实例初始化中使用的特殊方法
- 方法区可以是固定大小,也可以根据计算需要进行扩展,如果不需要较大的方法区,可以进行收缩。方法区的内存不需要是连续的
- 在jdk8以前,方法区称为永久代,jdk8及以后称为元空间
- 元空间使用的是本地内存
设置方法区大小
- jdk8以前:-XX:PermSize设置永久代初始大小、-XX:MaxPermSize设置永久代最大可分配空间
- jdk8及以后:-XX:MetaspaceSize设置元空间初始大小、-XX:MaxMetaspaceSize设置元空间最大可分配空间
如何解决OOM
- 通过内存映像工具对dump出来的堆存储快照进行分析,知道是出现的内存泄露还是内存溢出
- 如果是内存泄漏,可进一步通过工具查看泄漏对象到cc Roots 的引用链。于是就能找到泄漏对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们的。掌握了泄漏对象的类型信息,以及GC Roots引用链的信息,就可以比较准确地定位出泄漏代码的位置
- 如果不是内存泄漏,查看堆空间是否可以调大,检查是否存在生命周期过长的对象,尝试减少程序运行期的内存消耗
运行时常量池
- 一个运行时常量池是一个类或一个接口在字节码文件中常量池的表现
- 它包含多种类型的常量、从编译时已知的数值字面量、在运行时解析的方法和字段引用
- 运行时常量池的功能类似于传统编程语言的符号表
- 每个运行时常量池都是从Java虚拟机的方法区分配的
- 类或接口的运行时常量池是在Java虚拟机创建类或接口时构造的
方法区演进细节
- jdk1.7以前:有永久代,静态变量存放在永久代上
- jdk1.7:有永久代,字符串常量池、静态变量移除,保存在堆中
- jdk8及以后:无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但是字符串常量池、静态变量仍然在堆中
永久代为什么被替换?
- 为永久代设置空间大小很难确定
- 对永久代调优很困难
GC
方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。方法区中的常量池主要存放两类常量:字面量和符号引用.只要常量池中的常量没有被任何引用,就会被回收
字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。
符号引用包含以下三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
判断一个类是否属于"不再使用的类"需要满足以下三个条件,这样Java虚拟机允许对这样的类进行回收 - 该类所有的实例都已经被回收
- 加载该类的类加载器已经被回收
- 该类对于的java.lang.Class对象没有在任何地方被引用