方法区演进细节与垃圾回收

方法区演进细节

永久代演进过程:

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

    JDK6 :方法区由永久代实现,使用 JVM 虚拟机内存(虚拟的内存)。

    JDK7 :方法区由永久代实现,使用 JVM 虚拟机内存。

    JDK8 :方法区由元空间实现,使用物理机本地内存。

永久代为什么要被元空间替代?

官方文档:http://openjdk.java.net/jeps/122

  1. 随着 Java8 的到来,HotSpot VM 中再也见不到永久代了。但是这并不意味着类的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域,这个区域叫做元空间(Metaspace)。
  2. 由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。
  3. 这项改动是很有必要的,原因有:
  • 为永久代设置空间大小是很难确定的。在某些场景下,如果动态加载类过多,容易产生 Perm 区的 OOM 。比如某个实际 Web 工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。如:Exception in thread ‘dubbo client x.x connector’ java.lang .OutOfMemoryError:PermGen space,而元空间和永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。 因此,默认情况下,元空间的大小仅受本地内存限制。

  • 对永久代进行调优是很困难的。方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再用的类型,方法区的调优主要是为了降低 Full GC。

字符串常量池

字符串常量池 StringTable 为什么要调整位置?

  • JDK7 中将 StringTable 放到了堆空间中。因为永久代的回收效率很低,在 Full GC 的时候才会执行永久代的垃圾回收,而 Full GC 是老年代的空间不足、永久代不足时才会触发;
  • 这就导致StringTable回收效率不高,而开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足,放到堆里,能及时回收内存。

静态变量放在哪里

对象实体在哪里放着?

/*** 结论:* 1、静态引用对应的对象实体(也就是这个new byte[1024 * 1024 * 100])始终都存在堆空间,* 2、只是那个变量(相当于下面的arr变量名)在 JDK6,JDK7,JDK8 存放位置中有所变化** jdk7:* -Xms200m -Xmx200m -XX:PermSize=300m -XX:MaxPermSize=300m -XX:+PrintGCDetails* jdk 8:* -Xms200m -Xmx200m -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=300m -XX:+PrintGCDetails*/
public class StaticFieldTest {private static byte[] arr = new byte[1024 * 1024 * 100];//100MBpublic static void main(String[] args) {System.out.println(StaticFieldTest.arr);}
}

JDK6 环境下:

JDK7 环境下:

JDK8 环境下:

变量(名)存放在哪里?

用JHSDB工具来进行分析:

public class StaticObjTest {static class Test {static ObjectHolder staticObj = new ObjectHolder();ObjectHolder instanceObj = new ObjectHolder();void foo() {ObjectHolder localObj = new ObjectHolder();System.out.println("done");}}private static class ObjectHolder {}public static void main(String[] args) {Test test = new StaticObjTest.Test();test.foo();}
}

JDK6 环境下:

  1. staticObj 随着 Test 的类型信息存放在方法区;

  2. instanceObj 随着 Test 的对象实例存放在Java堆;

  3. localObject (局部变量)则是存放在 foo() 方法栈帧的局部变量表中;

  4. 测试发现:三个对象的数据在内存中的地址都落在 Eden 区范围内,所以结论:只要是对象实例必然会在Java堆中分配。

  1. 0x00007f32c7800000(Eden区的起始地址) —- 0x00007f32c7b50000(Eden区的终止地址),
  2. 可以发现三个变量都在这个范围内,
  3. 所以可以得到上面结论。
  1. 接着,找到了一个引用该 staticObj 对象的地方,是在一个 java.lang.Class 的实例里,并且给出了这个实例的地址,通过 Inspector 查看该对象实例,可以清楚看到这确实是一个 java.lang.Class 类型的对象实例,里面有一个名为 staticobj 的实例字段:
  • 从《Java虚拟机规范》所定义的概念模型来看,所有 Class 相关的信息都应该存放在方法区之中,但方法区该如何实现,《Java虚拟机规范》并未做出规定,这就成了一件允许不同虚拟机自己灵活把握的事情。JDK7 及其以后版本的 HotSpot 虚拟机选择把静态变量与类型在 Java 语言一端的映射 Class 对象存放在一起,存储于 Java 堆之中,从实验中也明确验证了这一点。

方法区的垃圾回收

  1. .有些人认为方法区(如 Hotspot 虚拟机中的元空间或者永久代)是没有垃圾收集行为的,其实不然。《Java虚拟机规范》对方法区的约束是非常宽松的,提到过可以不要求虚拟机在方法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如 JDK11 时期的ZGC 收集器就不支持类卸载)。

  2. .一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。但是这部分区域的回收有时又确实是必要的。以前 sun 公司的 Bug 列表中,曾出现过的若干个严重的 Bug 就是由于低版本的 HotSpot 虚拟机对此区域未完全回收而导致内存泄漏。

  3. .方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。

    1.先来说说方法区内常量池之中主要存放的两大类常量:字面量和符号引用。字面量比较接近Java语言层次的常量概念,如文本字符串、被声明为final的常量值等。而符号引用则属于编译原理方面的概念,包括下面三类常量:

    • 类和接口的全限定名
    • 字段的名称和描述符
    • 方法的名称和描述符

    2.HotSpot 虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收。

    3.回收废弃常量与回收 Java 堆中的对象非常类似。(关于常量的回收比较简单,重点是类的回收)

方法区的类卸载:

  1. 判定一个常量是否“废弃”还是相对简单,而要判定一个类型是否属于“不再被使用的类”的条件就比较苛刻了。需要同时满足下面三个条件:

    • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类及其任何派生子类的实例;
    • 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP 的重加载等,否则通常是很难达成的;
    • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
  2. Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是“被允许”,而并不是和对象一样,没有引用了就必然会回收。关于是否要对类型进行回收,HotSpot 虚拟机提供了-Xnoclassgc 参数进行控制,还可以使用 -verbose:class 以及 -XX:+TraceClass-Loading、-XX: +TraceClassUnLoading 查看类加载和卸载信息。

  3. 在大量使用反射、动态代理、CGLib 等字节码框架,动态生成 JSP 以及 OSGi 这类频繁自定义类加载器的场景中,通常都需要 Java 虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力。

运行时数据区总结

常见面试题

  1. 百度
  • 三面:说一下JVM内存模型吧,有哪些区?分别干什么的?
  1. 蚂蚁金服:
  • Java8的内存分代改进
  • JVM内存分哪几个区,每个区的作用是什么?
  • 一面:JVM内存分布/内存结构?栈和堆的区别?堆的结构?为什么两个survivor区?
  • 二面:Eden和survior的比例分配。
  1. 小米:
  • jvm内存分区,为什么要有新生代和老年代?
  1. 字节跳动:
  • 二面:Java的内存分区
  • 二面:讲讲vm运行时数据库区。
  • 什么时候对象会进入老年代?
  1. 京东:
  • JVM的内存结构,Eden和Survivor比例。
  • JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和survivor。
  1. 天猫:
  • 一面:Jvm内存模型以及分区,需要详细到每个区放什么。
  • 一面:JVM的内存模型,Java8做了什么改变?
  1. 拼多多:
  • JVM内存分哪几个区,每个区的作用是什么?
  1. 美团:
  • java内存分配
  • jvm的永久代中会发生垃圾回收吗?
  • 一面:jvm内存分区,为什么要有新生代和老年代?

JVM运行时数据区---方法区(演变和垃圾回收)相关推荐

  1. 12.JDK1.8 JVM运行时数据区域概览、各区域介绍、程序计数器、Java虚拟机栈、本地方法栈、堆、堆空间内存分配(默认情况下)、字符串常量池、元数据区、jvm参数配置

    12.JDK1.8 JVM运行时数据区域概览 12.1.JDK1.8 JVM运行时数据区域概览 12.2.各区域介绍 12.3.各区域介绍 12.3.1.程序计数器 12.3.2.Java虚拟机栈 1 ...

  2. 【JVM学习-3.6】JVM运行时数据区--方法区

    文章目录 1. 栈.堆.方法区的交互关系 2. 方法区的理解 2.1 方法区在哪? 2.2 方法区的基本理解 2.3 Hotspot中方法区的演进 3. 设置方法区大小与OOM 3.1 jdk7及以前 ...

  3. JVM运行时数据区概览

    在学习JVM之前我们需要明确的是,我们所学习的是JVM的一个规范,在实际中有很多不同种类的虚拟机来实现这一种规范.其次JVM运行时数据区和JMM的区别我们要搞清楚,不能将JMM理解为JVM运行是数据区 ...

  4. JVM运行时数据区分析

    #1.概述 整个JVM构成⾥⾯,由三部分组成:类加载器机制.运⾏时数据区.执⾏引擎. #2.JVM运行时数据区的规范 我们来聊聊这个规范怎么理解,目前运行数据区共分为了方法区.堆.虚拟机栈.本地方法栈 ...

  5. JVM运行时数据区和各个区域的作用

    一.JVM主要分为5个核心区域(6个子区域),分别是: 程序计数器 Java虚拟机栈 本地方法栈 Java堆 方法区 *运行时常量池(属于"方法区"的一部分) 二.各个区域作用和描 ...

  6. Java -----JVM运行时数据区

    一.JVM体系结构 想要了解运行时数据区,先关注一下JVM的体系结构,知道数据区在JVM的整体位置和作用. 二.JVM运行时数据区 1.程序计数器 一块较小的内存空间,它是当前线程所执行的字节码的行号 ...

  7. 掌握JVM 运行时数据区,其实不是很难,加薪也是要技巧可言的!!!

    一.概念 Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分. 而内存模型(Java Memory Model,简称 JMM )是定义了线程 ...

  8. Java内存管理:Java内存区域 JVM运行时数据区

    Java内存管理:Java内存区域 JVM运行时数据区 在前面的一些文章了解到javac编译的大体过程.Class文件结构.以及JVM字节码指令. 下面我们详细了解Java内存区域:先说明JVM规范定 ...

  9. Java8 JVM运行时数据区概述 (极其详细长文)

    文章目录 运行时数据区概述 JVM中的线程说明 PC寄存器(PC Register) PC寄存器介绍 使用举例 问题:使用PC寄存器存储字节码指令地址有什么用?为什么使用PC寄存器存储? 问题:为什么 ...

  10. 一篇文章带你快速理解JVM运行时数据区 、程序计数器详解 (手画详图)值得收藏!!!

    受多种情况的影响,又开始看JVM 方面的知识. 1.Java 实在过于内卷,没法不往深了学. 2.面试题问的多,被迫学习. 3.纯粹的好奇. 很喜欢一句话:"八小时内谋生活,八小时外谋发展. ...

最新文章

  1. 【小白学习PyTorch教程】八、使用图像数据增强手段,提升CIFAR-10 数据集精确度
  2. diy一下devise的验证
  3. python导入同目录下的模块_如何从同一目录下的模块导入?
  4. Java中多线程的使用!!
  5. 记一次免费让网站启用HTTPS的过程
  6. 当代开发者的六大真实现状,你被哪一个场景“戳中”了?
  7. session_cache_limiter(private,must-revalidate)是什么意思
  8. python爬虫从入门到放弃-【爬虫】python爬虫从入门到放弃
  9. C# 设置开机自启动
  10. [VOSViewer] 合并同义词、删除指定词语
  11. 【db2】db2错误代码大全-SQLCODE
  12. 树的前序、中序、后序遍历 | Tree Walk | C/C++实现
  13. Scratch少儿编程(三)外观模块
  14. INDEX函数与MATCH函数嵌套使用技巧
  15. 用Java写一个简易五子棋游戏
  16. Hive数据导出为csv、tsv文件的几种方法
  17. Windows系统怎么查看电脑操作系统位数
  18. 【架构】分布式系统及相关技术栈初了解
  19. javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
  20. 莫道君行早更有早来人(一)黄页

热门文章

  1. 管理-Tomcat和Resin如何配置对指定后缀文件(如:.pptx)下载支持
  2. 你属于程序员中的哪种人?
  3. HDU 1024:Max Sum Plus Plus(DP)
  4. create-react-app 2.0中使用antd(eject)
  5. Java练习 SDUT-2737_小鑫の日常系列故事(六)——奇遇记
  6. Innodb与MySQL各自功能
  7. PIX的精彩 --- IKE的两个阶段
  8. Visual Basic 2005 – 如何播放剪贴簿中的音效数据
  9. 使用堆内内存HeapByteBuffer的注意事项
  10. 面试必备:4种经典限流算法讲解