11-方法区的演进以及StringTable的调整

简介: 《Java虚拟机规范》中明确说明:“尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。”但对于 HotSpotJVM 而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。 所以,方法区看作是一块独立于Java堆的内存空间。

13-方法区的演进以及StringTable的调整

学习路线图 (2)_20210722224513.png

方法区主要存放的是『Class』,而堆中主要存放的是『实例化的对象』

  • 方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。
  • 方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
  • 方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
  • 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:java.lang.OutofMemoryError:PermGen space 或者java.lang.OutOfMemoryError:Metaspace
    • 加载大量的第三方的jar包
    • Tomcat部署的工程过多(30 -- 50个)
    • 大量动态的生成反射类
  • 关闭JVM就会释放这个区域的内存。

1.HotSpot中方法区的演进

在jdk7及以前,习惯上把方法区,称为永久代。jdk8开始,使用元空间取代了永久代。

JDK 1.8后,元空间存放在堆外内存中。

本质上,方法区和永久代并不等价。仅是对hotspot而言的。《Java虚拟机规范》对如何实现方法区,不做统一要求。例如:BEAJRockit / IBM J9 中不存在永久代的概念。

现在来看,当年使用永久代,不是好的idea。导致Java程序更容易oom(超过-XX:MaxPermsize上限)


 而到了JDK8,终于完全废弃了永久代的概念,改用与JRockit、J9一样在本地内存中实现的元空间(Metaspace)来代替:

​ 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代最大的区别在于:元空间不在虚拟机设置的内存中,而是使用本地内存。

  永久代、元空间二者并不只是名字变了,内部结构也调整了。

  根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OOM异常。

2.方法区内部结构

《深入理解Java虚拟机》书中对方法区(Method Area)存储内容描述如下:它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。

这里我们重点关注掌握运行时常量池

3.运行时常量池

方法区,内部包含了运行时常量池

要弄清楚方法区的运行时常量池,需要理解清楚classFile中的常量池

  • 常量池

    一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述符信息外,还包含一项信息就是常量池表(Constant Pool Table),包括各种字面量和对类型、域和方法的符号引用

​ 常量池、可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量 等类型。

  • 运行时常量池

    运行时常量池(Runtime Constant Pool)是方法区的一部分。

  常量池表(Constant Pool Table)是Class文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

  运行时常量池,在加载类和接口到虚拟机后,就会创建对应的运行时常量池。

  JVM为每个已加载的类型(类或接口)都维护一个常量池。池中的数据项像数组项一样,是通过索引访问的。

  运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期解析后才能够获得的方法或者字段引用。此时不再是常量池中的符号地址了,这里换为真实地址。

  运行时常量池,相对于Class文件常量池的另一重要特征是:具备动态性。

  运行时常量池类似于传统编程语言中的符号表(symboltable),但是它所包含的数据却比符号表要更加丰富一些。

  当创建类或接口的运行时常量池时,如果构造运行时常量池所需的内存空间超过了方法区所能提供的最大值,则JVM会抛outofMemoryError异常。

4.方法区的演进细节

首先明确:只有Hotspot才有永久代。BEA JRockit、IBM J9等来说,是不存在永久代的概念的。原则上如何实现方法区属于虚拟机实现细节,不受《Java虚拟机规范》管束,并不要求统一。

Hotspot中方法区的变化:

版本 内容
JDK1.6及以前 有永久代,静态变量存储在永久代上
JDK1.7 有永久代,但已经逐步 “去永久代”,字符串常量池,静态变量移除,保存在堆中
JDK1.8 无永久代,类型信息,字段,方法,常量保存在本地内存的元空间,但字符串常量池、静态变量仍然在堆中。

JDK6的时候:

JDK7的时候:


JDK8的时候,元空间大小只受物理内存影响:

5.StringTable

StringTable叫做字符串常量池,用于存放字符串常量,这样当我们使用相同的字符串对象时,就可以直接从StringTable中获取而不用重新创建对象。

StringTable为什么要调整位置

jdk7中将StringTable放到了堆空间中。因为永久代的回收效率很低,在full gc的时候才会触发。而full gc是老年代的空间不足、永久代不足时才会触发。

这就导致stringTable回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存。

关于StringTable的深入讲解、垃圾回收、性能调优以及字符串的特点会结合视频进行讲解发放

String相关的面试题如下:(可以自行思考)

String s1 = "a";
String s2 = "b";
String s3 = "a" + "b";
String s4 = s1 + s2;
String s5 = "ab";
String s6 = s4.intern();

// 问
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);

String x2 = new String("c") + new String("d");
String x1 = "cd";
x2.intern();

// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢
System.out.println(x1 == x2);

6.JVM内存相关的几个核心参数

在JVM内存分配中,有几个参数是比较核心的,我们总结归纳在一起:

指令 描述
-Xms Java堆内存的大小
-Xmx Java堆内存的最大大小
-Xmn Java堆内存中的新生代大小,扣除新生代剩下的就是老年代的内存大小
-XX:PermSize 永久代大小
-XX:MaxPermSize 永久代最大大小
-Xss 每个线程的栈内存大小

7.系统启动的参数设置

我们可以在启动某个类或项目的时候进行VM Options参数设置:

比如

运行之前测试过的代码,结果跟设置一致:

总结

最后我们通过一个详细图对整个JVM的内存结构做一个总结:

目录
相关文章
|
4月前
|
存储 缓存 算法
(五)JVM成神路之对象内存布局、分配过程、从生至死历程、强弱软虚引用全面剖析
在上篇文章中曾详细谈到了JVM的内存区域,其中也曾提及了:Java程序运行过程中,绝大部分创建的对象都会被分配在堆空间内。而本篇文章则会站在对象实例的角度,阐述一个Java对象从生到死的历程、Java对象在内存中的布局以及对象引用类型。
126 8
|
5月前
|
存储 缓存 监控
深入解析JVM内存分配优化技术:TLAB
深入解析JVM内存分配优化技术:TLAB
|
6月前
|
存储 算法 安全
深度解析JVM世界:JVM内存分配
深度解析JVM世界:JVM内存分配
|
6月前
|
存储 缓存 算法
JVM【带着问题去学习 01】什么是JVM+内存结构+堆内存+堆内存参数(逃逸分析)
JVM【带着问题去学习 01】什么是JVM+内存结构+堆内存+堆内存参数(逃逸分析)
56 0
|
存储 缓存 算法
jvm之java内存区域(一)
jvm之java内存区域(一)
79 0
|
存储 Dubbo Java
jvm之方法区演变及方法区的GC解读
jvm之方法区演变及方法区的GC解读
|
算法 Java Android开发
JVM内存溢出及其解决方案
Java虚拟机(JVM)是执行Java程序的核心环境,它管理着应用程序的内存分配和回收。然而,在某些情况下,应用程序可能会遇到内存溢出的问题,即JVM无法为应用程序分配足够的内存空间。本文将探讨JVM内存溢出的原因以及解决方案,帮助开发人员更好地理解和处理这一问题。
300 0
|
存储 Java
JVM - 结合代码示例彻底搞懂Java内存区域_对象在堆-栈-方法区(元空间)之间的关系
JVM - 结合代码示例彻底搞懂Java内存区域_对象在堆-栈-方法区(元空间)之间的关系
124 0
|
算法 Java 微服务
JVM垃圾回收器:分代堆内存管理,堆设计+分代边界+回收设计思路
垃圾回收器是JVM中最重要的组件之一,几乎每一个JDK的大版本都对垃圾回收进行重大的更新。另外,由于JDK发布策略的改变,在最近3年的版本发布中,每一个大版本都至少合入一个(甚至数个)关于垃圾回收的JEP。
|
存储 算法 Java
jvm自动内存管理机制 之 java内存区域
jvm自动内存管理机制 之 java内存区域
114 0