JVM内存模型-方法区和垃圾回收

笔记均来自于尚硅谷官方视频,为了复习,无其他目的
视频笔记来源

一、 堆、栈、方法区的交互关系

1.运行时数据区结构图
在这里插入图片描述

在这里插入图片描述

2.堆、栈、方法区的交互关系
在这里插入图片描述

注意:局部变量表中的对象引用指向堆空间中的实体,堆空间中有对象类型数据指针指向方法区中的对象类型数据,即对象是哪个类new的。只要是对象实例必然会在Java堆中分配。

在这里插入图片描述

二、方法区的理解

1.方法区可以看作是一块独立于Java堆的内存空间。

2.方法区(Method Area),是各个线程共享的内存区域

3.方法区在JVM启动时被创建,并且它的实际的物理内存空间中是不连续

4.方法区的大小可以选择固定大小或者可拓展

5.方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,方法区无法满足新的内存分配需求时,导致方法区溢出,虚拟机同样会抛出内存溢出错误(OOM异常):.java.lang.OutOfMemoryError:PermGen space(jdk 7及以前) 或者java.lang,OutOfMemoryError:Metaspace(jdk 1.8之后),比如:

加载大量的第三方jar包;
Tomcat部署的工程过多;
大量动态生成反射类;

7.关闭JVM就会释放这个区域的内存

三、方法区的内部结构

在这里插入图片描述

用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。

1.类型信息
在这里插入图片描述

2.域信息(成员变量)

在这里插入图片描述

3.方法信息
在这里插入图片描述

5.non-final的类变量
在这里插入图片描述

6.运行时常量池

(1).常量池
在这里插入图片描述

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

几种在常量池内存储的数据类型包括:
在这里插入图片描述

②一个 java 源文件中的类、接口,编译后产生一个字节码文件。而 Java 中的字节码需要数据支持,通常这种数据会很大不能直接存到字节码里,则可以存到常量池,这个字节码包含了指向常量池的引用。在动态链接的时候会用到运行时常量池.

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

(2).运行时常量池

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

常量池表(Constant Pool Table)是Class文件的一部分,用于存放编译期生成的各种字面量与符号引用,常量池中的数据项像数组项一样,是通过索引访问的。

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

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

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

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

四、方法区实现的演进细节变化。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.变化:永久代------>元空间
在这里插入图片描述

在jdk 7及以前,习惯上把方法区称为永久代,仅是对hotSpot而言的。

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

方法区在jdk7及jdk8的落地实现

在这里插入图片描述

2.方法区与元空间的区别

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。

不过元空间与永久代最大的区别在于:元空间不再虚拟机设置的内存中,而是使用本地内存

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

3.只有HotSpot才有永久代。现在来看,使用永久代,更容易导致Java程序OOM(超过-XX:MaxPermSize上限)。

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

4.Hotspot中方法区的变化:

在这里插入图片描述

5.永久代为什么要被元空间替换

随着Java8的到来,永久代替换为元空间( Metaspace )。

由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。这项改动是很有必要的,原因有:

①为永久代设置空间大小是很难确定的。

元空间与永久代最大的区别在于:元空间不再虚拟机设置的内存中,而是使用本地内存

永久代使用的是虚拟内存,在某些场景下,如果动态加载类过多,容易产生PermGenspace的OOM。比如某个实际Web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。
"Exception in thread’ dubbo client x.x connector’java.lang.OutOfMemoryError: PermGenspace"

②对永久代进行调优是很困难的。
如果要不断的进行full GC 就要不停的暂停用户线程,用户线程等待时间(STW)就会边长。

6.StringTable (字符串常量表)为什么要调整
jdk7中将StringTable放到了堆空间中。因为永久代的回收效率很低,在full gc的时候才会触发。而full GC 是老年代的空间不足、永久代不足时才会触发。这就导致了StringTable回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存.

五、设置方法区大小与OOM

1.方法区的大小不必是固定的,jvm可以根据应用的需要动态调整
在这里插入图片描述

jdk7及以前:

在这里插入图片描述
当JVM加载的类信息容量超过了这个值,会报异常OutOfMemoryError : PermGen space

jdk8及以后:

元数据区大小默认值依赖于平台。
-XX:MetaspaceSize: 设置初始的元空间大小。
对于64位的服务器端JVM来说,其默认的初始的元空间大小为21MB.

-XX:MaxMetaspaceSize的值是-1, 即没有限制。

在这里插入图片描述
通过垃圾回收器的日志可以观察到Full GC多次调用。为了避免频繁地GC,建议将-XX :MetaspaceSize设置为一个相对较高的值。

2.方法区的OOM
在这里插入图片描述

六、方法区的垃圾回收

在这里插入图片描述

方法区内常量池之中主要存放的两大类常量:字面量和符号引用。

字面量,如文本字符串、被声明为final的常量值等。而符号引用则属于编译原理方面的概念,包括下面三类常量:

1、类和接口的全限定名
2、字段的名称和描述符
3、方法的名称和描述符

HotSpot虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收
在这里插入图片描述

七、常见面试题

百度
三面:说一下JVM内存模型吧,有哪些区?分别干什么的?

蚂蚁金服:
Java8的内存分代改进
JVM内存分哪几个区,每个区的作用是什么?
一面: JVM内存分布/内存结构?栈和堆的区别?堆的结构?为什么两个survivor区?
二面: Eden和Survior的比例分配

小米:
jvm内存分区,为什么要有新生代和老年代

字节跳动:
二面: Java的内存分区
二面:讲讲jvm运行时数据库区
什么时候对象会进入老年代?

京东:
JVM的内存结构,Eden和Survivor比例 。
JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor。

天猫:
一面: Jvm内存模型以及分区,需要详细到每个区放什么。
一面: JVM的内存模型,Java8做了什么修改

拼多多:
JVM内存分哪几个区,每个区的作用是什么?

美团:
java内存分配
jvm的永久代中会发生垃圾回收吗?
一面: jvm内存分区,为什么要有新生代和老年代?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值