jvm-运行时数据区(运行时数据区概述、方法区)

运行时数据区概述

JVM运行时数据区规范

在这里插入图片描述
jdk1.7之前,HotSpot虚拟机对于方法区的实现称之为“永久代”, Permanent Generation 。
jdk1.8之后,HotSpot虚拟机对于方法区的实现称之为“元空间”, Meta Space 。
方法区是Java虚拟机规范中的定义,是一种规范,而永久代和元空间是 HotSpot 虚拟机不同版本的 两种实现。

Hotspot运行时数据区

在这里插入图片描述

分配jvm内存空间
  • 分配堆的大小
–Xms(堆的初始容量) 
-Xmx(堆的最大容量)
  • 分配方法区的大小
-XX:PermSize 永久代的初始容量 
-XX:MaxPermSize 永久代的最大容量
-XX:MetaspaceSize 元空间的初始大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释 放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适 当提高该值。
-XX:MaxMetaspaceSize 最大空间,默认是没有限制的。
  • 除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
-XX:MinMetaspaceFreeRatio 在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集 
-XX:MaxMetaspaceFreeRatio 在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
  • 分配线程空间的大小
-Xss:为jvm启动的每个线程分配的内存大小,默认JDK1.4中是256K,JDK1.5+中是1M

方法区

方法区存储内容
  • 1:类型信息
    • 类型的全限定名
    • 超类的全限定名
    • 直接超接口的全限定名
    • 类型标志(该类是类类型还是接口类型)
    • 类的访问描述符(public、private、default、abstract、final、static)
  • 2:类型的常量池
    存放该类型所用到的常量的有序集合,包括直接常量(如字符串、整数、浮点数的常量)和对其他类 型、字段、方法的符号引用。常量池中每一个保存的常量都有一个索引,就像数组中的字段一样。因为常量池中保存着所有类型使用到的类型、字段、方法的字符引用,所以它也是动态连接的主要对象(在动态链接中起到核心作用)。
  • 3:字段信息(类声明的所有字段)
    • 字段修饰符(public、protect、private、default)
    • 字段的类型
    • 字段名称
  • 4:方法信息
    方法信息中包含类的所有方法,每个方法包含以下信息:
    • 方法修饰符
    • 方法返回类型
    • 方法名
    • 方法参数个数、类型、顺序等
    • 方法字节码
    • 操作数栈和该方法在栈帧中的局部变量区大小
    • 异常表
  • 5:类变量
    指该类所有对象共享的变量,即使没有任何实例对象时,也可以访问的类变量。它们与类进行绑定。
  • 6:指向类加载器的引用
    每一个被JVM加载的类型,都保存这个类加载器的引用,类加载器动态链接时会用到。
  • 7:指向Class类实例的引用
    类加载的过程中,虚拟机会创建该类型的Class实例,方法区中必须保存对该对象的引用。通过Class.forName(String className)来查找获得该实例的引用,然后创建该类的对象。
  • 8:方法表
    为了提高访问效率,JVM可能会对每个装载的非抽象类,都创建一个数组,数组的每个元素是实例可能调用的方法的直接引用,包括父类中继承过来的方法。这个表在抽象类或者接口中是没有的。
  • 9:运行时常量池
    Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种字面常量和符号引用,这部分内容被类加载后进入方法区的运行时常量池中存放。
    运行时常量池相对于Class文件常量池的另外一个特征具有动态性,可以在运行期间将新的常量放入池中(典型的如String类的intern()方法)。
永久带与元空间区别

永久代和元空间存储位置和存储内容的区别:

  • 存储位置不同,永久代物理是是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存
  • 存储内容不同,元空间存储类的元信息,[静态变量]和[常量池]等并入堆中。相当于永久代的数据被分到 了堆和元空间中。
    JVM 的内存划分,也清楚了 JDK 8 中永久代向元空间的转换。
    不过大家应该都有一个疑问,就是为什么要做这个转换?带着这个疑问,最后给大家总结以下几点原
    因:
    • 1:字符串存在永久代中,容易出现性能问题和永久代内存溢出
    • 2:类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢 出,太大则容易导致老年代溢出
    • 3:永久代会为 GC 带来不必要的复杂度,并且回收效率偏低
    • 4:Oracle 可能会将HotSpot 与 JRockit 合二为一
      其实,移除永久代的工作从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。
方法区异常示例
类加载导致OOM异常
代码
public class Test {}

import java.io.File; 
import java.net.URL; 
import java.net.URLClassLoader; 
import java.util.ArrayList; i
mport java.util.List; 
public class PermGenOomMock{ 
	public static void main(String[] args) {
		URL url = null; 
		List<ClassLoader> classLoaderList = new ArrayList<ClassLoader>(); 
		try {
			url = new File("/tmp").toURI().toURL(); 
			URL[] urls = {url}; 
			while (true){ 
				ClassLoader loader = new URLClassLoader(urls); 
				classLoaderList.add(loader); 
				loader.loadClass("Test"); 
			} 
		} catch (Exception e) { 
			e.printStackTrace(); 
		} 
	} 
}
JDK1.7

指定的 PermGen 区的大小为 8M
在这里插入图片描述
绝大部分 Java 程序员应该都见过 "java.lang.OutOfMemoryError: PermGen space "这个异常。这里的 “ PermGen space ”其实指的就是方法区。由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。最典型的场景就是,在 jsp 页面比较多的情况, 容易出现永久代内存溢出

JDK1.8+

现在我们在 JDK 8下重新运行一下案例代码,不过这次不再指定 PermSize 和 MaxPermSize 。而是指定 MetaSpaceSize 和 MaxMetaSpaceSize 的大小。输出结果如下
在这里插入图片描述
从输出结果,我们可以看出,这次不再出现永久代溢出,而是出现了元空间的溢出。

字符串OOM异常
案例代码
import java.util.ArrayList; 
import java.util.List; 
public class StringOomMock { 
	static String base = "string"; 
	public static void main(String[] args) { 
		List<String> list = new ArrayList<String>(); 
		for (int i=0;i< Integer.MAX_VALUE;i++){ 
			String str = base + base; 
			base = str; 
			list.add(str.intern()); 
		} 
	} 
}
JDK1.6
  • JDK 1.6下,会出现永久代的内存溢出。
    在这里插入图片描述
JDK1.7
  • 会出现堆内存溢出。结论是:JDK 1.7 已经将字符串常量由永久代转移到堆中。
    在这里插入图片描述
JDK1.8+
  • 会出现堆内存溢出,并且显示 JDK 1.8中 PermSize 和 MaxPermGen 已经无效。因此,可以验证 JDK 1.8 中已经不存在永久代的结论。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值