尚硅谷2020最新版宋红康JVM教程学习笔记 四

点击查看合集

堆 栈 方法区的关系

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

什么是方法区

在java虚拟机规范中明确说明:尽管方法区逻辑上是堆的一部分,但是一些简单的实现可能不会选择进行垃圾回收或进行压缩/对于HotSpotJVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。所以方法区看作是一块独立于java堆的内存空间。
在这里插入图片描述
1.方法区与java堆一样,是各个线程共享的内存区域
2.方法区在jvm启动的时候创建,并且他的实际的物理内存空间中与java堆区一样都可以是不连续的。
3.方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展
4.方法去的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出OutOfMemoryError

在JDK7及以前,习惯上把方法区,称为永久代。JDK8开始,使用元空间取代了永久代。
永久代/元空间是方法区的一种具体实现(HotSport JVM)因此不能说永久代/元空间与方法区等价。
在这里插入图片描述
永久代使用JVM虚拟机的内存(使用XX:MaxPermSize 设置永久代/方法区的大小),使Java程序很容易OOM。元空间使用本地的物理内存,而不是虚拟机内存。

设置永久代(方法区)的大小

XX:PermSize= (初始大小)
XX:MaxPermSize= (最大)

设置元空间(默认是动态的)

元空间可以设置默认大小。
XX:MatespaceSize= (初始大小)
当方法区的内存超过初始大小时,将会触发Full GC,Full GC(Full GC将会卸载没用的类)之后会调整MatespaceSize的大小。新的Matespace的值取决于Full GC释放了多少空间。如果释放的空间过多,此值将会变小。如果释放的空间不多,那么在不超过MaxMetaspaceSize的前提下会适当提高该值。(MatespaceSize也叫高水位限)
XX:MaxMataspaceSize= (最大)如果设置为-1则代表没有限制

方法区的内部结构

在这里插入图片描述
方法区:用于存储已被虚拟机加载的类型信息,常量,静态变量、即时编译器编译后的代码缓存等
在这里插入图片描述

类型信息

对每个加载的类型(类Class 接口interface 枚举enum 注解 annotation),jvn必须在方法区中存储以下类型信息
1.这个类型的完整有效名称(全名=包名.类名)
2.这个类型直接父类的完整有效名(对于interface或者Object都没有父类)
3.这个类型的修饰符(public abstract final 的某个子集)
4.这个类型直接接口的一个有序列表

域(Field 属性)信息

1.JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序
2.域的相关信息包括:域名称、域类型、域修饰符(public private protected static final volatile transient的某个子集)

方法信息

JVM必须保存所有方法的以下信息,同域信息一样包括声明顺序
1.方法名称
2.方法的返回类型(或 void)
3.方法参数的数量和类型(按顺序)
4.方法的修饰符(public private protected static final synchronized native abstract的一个子集)
5.方法的字节码以及操作数栈和局部变量表的大小(abstract 和native方法除外)
6.异常表(abstract 和native 方法除外)每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引。

常量池

字节码文件内部包含了常量池,把常量池加载到方法区后,相应的结构就是运行时常量池。
一个有效的字节码文件中除了包含类的版本信息、字段、方法、以及接口等描述信息外,还包含一项信息那就是常量池表,它包括各种字面量和 类型 域 方法 的符号引用。

运行时常量池

1.运行时池常量池是方法区的一部分
2.常量池比与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池。
3.运行时常量池,在加载类和接口到虚拟机后,就会创建对应的运行时常量池。
4.JVM为每个已加载的类型都维护一个常量池。池中的数据项像数组项一样,是通过索引访问的。
5.运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期解析后才能够获得的方法或者字段引用。此时不再是常量池中的符号地址了,这里换为真实地址。运行时常量池,相对于class文件常量池的另一重要特征是:具备动态性。

方法区的演进

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

为什么要把永久代替换成元空间

1.不容易确定永久代的大小
2.永久代不容易调优

StringTable(字符串常量表)为什么要移出永久代

因为开发中会有大量字符串被创建,如果字符串保存在永久代,那么只有执行Full GC时才会对字符串进行回收,回收效率低。放在堆中,能及时回收内存。

对象创建过程

1.判断对象对应的类是否加载、链接(验证 准备 解析 )初始化
2.在堆中为对象分配内存
3.处理并发安全问题(TLAB或者加锁)
4.初始化分配到的空间(给实例变量赋默认值)
5.设置对象的对象头
6.对象初始化(显式初始化->代码块中初始化->构造器中初始化)

对象的内存布局

  1. 对象头
    对象头中包括运行时元数据和类型指针
  2. 实例数据
    父类和本身拥有的字段。相同宽度的字段被放在一起且父类的变量在子类之前
  3. 对象填充
    保证对象的内存大小是8的倍数。
    在这里插入图片描述

对象访问定位

1.句柄访问法
在这里插入图片描述
缺点:需要在堆中开辟一块空间 需要两次访问
优点:稳定,堆中对象地址改变的时候,只需要改相应句柄指向的地址。
2.直接指针法(HotSport使用)
在这里插入图片描述
缺点:需要经常更改局部变量表中对象的引用。
优点:一次访问 节省空间

直接内存(了解)

直接内存指的是直接分配本地内存空间(allocateDirect)
直接内存访问速度更快

猜你喜欢

转载自blog.csdn.net/qq_30033509/article/details/110749783