【jvm】6-方法区

  • 方法区也存在垃圾回收。

运行时数据期结构图

在这里插入图片描述

  • 线程共享与否角度

在这里插入图片描述

栈、堆、方法区交互关系

代码上:

在这里插入图片描述

栈堆方法结构角度:

  • 栈 -> 堆中实例数据指针 -> 方法区中对应对象类型数据

在这里插入图片描述

  • 另一个情况

在这里插入图片描述

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

方法区

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

在这里插入图片描述

  • jdk7以前习惯把方法区称永久代,jdk8开始,使用元空间取代永久代

  • jdk8完全废弃永久代概念

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

  • 永久代、元空间二者并不只是名字变了,内部结构也调整了。根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OOM异常。

设置方法区大小与oom

jdk7以前:

-xx:PermSize    来设置永久代初始分配空间。默认值是20.75M
-XX:MaxPermSize 来设定永久代最大可分配空间。32位机器默认是64M,64位机器模式是82M

jdk8后:

-XX:Netaspacesize=10om
-XX:MaxMetaspacesize=100m    值是-1 = 没有限制

方法区内存结构

在这里插入图片描述

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

类型信息

对每个加载的类型(类class、接口interface、枚举enum、注解annotation),JVM必须在方法区中存储以下类型信息:

  • 这个类型的完整有效名称(全名-包名.类名)
  • 这个类型直接父类的完整有效名(对于interface或是java.lang.0bject,都没有父类)
  • 这个类型的修饰符(public, abstract, final的某个子集)
  • 这个类型直接接口的一个有序列表

域(Field)信息

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

方法(Method)信息

JVM必须保存所有方法的以下信息,同域信息一样包括声明顺序:

  • 方法名称
  • 方法的返回类型(或void)·方法参数的数量和类型(按顺序)
  • 方法的修饰符(public, private, protected,static, final,synchronized, native, abstract的一个子集)
  • 方法的字节码(bytecodes)、操作数栈、局部变量表及大小(abstract和native方法除外)
  • 异常表( abstract和native方法除外)
    • 每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引

non-final的类变量

  • 静态变量和类关联在一起,随着类的加载而加载,它们成为类数据在逻辑上的一部分。
  • 类变量被类的所有实例共享,即使没有类实例时你也可以访问它。

补充说明:全局常量: static final

  • 被声明为final的类变量的处理方法则不同,每个全局常量在编译的时候就会被分配了。

运行时常量池

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

  • 字节码文件,内部包含了常量池。

  • 要弄清楚方法区,需要理解清楚classFile,因为加载类的信息都在方法区。

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

字节码-常量池 方法区-运行时常量池 字节码-加载-方法区

常量池

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

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

程序执行过程

public class MethodAreaDemo {
    public static void main(String[] args) {
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a + b);
    }
}
/* 
    译后字节码 stack=3,locals5, args_size=1
*/

在这里插入图片描述

pc寄存器存储当前线程下一条指令。

程序计数器为栈帧中记录当前执行指令,已其他栈帧切回来时可以知道执行到哪

方法区演进

版本说明
jdk1.6前有永久代(permanent generation), 静态变量存放在永久代上
jdk1.7有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存在堆中
jdk8及后无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆

永久代即方法区

  • jdk1.6

在这里插入图片描述

  • jdk1.8

在这里插入图片描述

方法区垃圾回收

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

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

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

  • 该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
  • 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如osGi、JSP的重加载等,否则通常是很难达成的。
  • 该类对应的java.lang.class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

对象实例

创建方式

在这里插入图片描述

创建步骤

在这里插入图片描述

实例化的过程
  • 加载类元信息
  • 为对象分配内存
  • 处理并发问题
  • 属性的默认初始化(零值初始化
  • 设置对象头的信息
  • 属性的显式初始化、代码块中初始化、构造器中初始化

内存布局

在这里插入图片描述

public class Customer{
    int id = 1001;
    String name;
    Account acct;
    {
        name = "匿名客户";
    }
    public Customer(){
        acct = new Account();
    }
}
class Account{
}


public class CustomerTest {
    public static void main(String[] args) {
        Customer cust = new Customer();
    }
}

在这里插入图片描述

访问定位

jvm如何通过栈帧中对象引用访问起到其内部对象实例?

在这里插入图片描述

访问方式:

  • 句柄 (实例栈空间地址稳定一些)

在这里插入图片描述

  • 直接 (效率好一些)

在这里插入图片描述

直接内存

元空间使用本地直接内存

概述

  • 不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。

  • 直接内存是在Java堆外的、直接向系统申请的内存区间。

  • 来源于NIO,通过存在堆中的DirectByteBuffer操作Native内存通常,访问直接内存的速度会优于Java堆。即读写性能高。

    • 因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存。、
    • Java的NIo库允许Java程序使用直接内存,用于数据缓冲区
  • 可能导致outofMemoryError异常

  • 由于直接内存在Java堆外,因此它的大小不会直接受限于-Xmx指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存。

  • 缺点

    • 分配回收成本较高
    • 不受JVM内存回收管理
  • 直接内存大小可以通过MaxDirectMemorySize设置如果不指定,默认与堆的最大值-xmx参数值一致

Unsafe类

通过Unsafe类可以分配内存,可以释放内存;

类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应。

可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;

public native long allocateMemory(long l);
public native long reallocateMemory(long l, long l1);
public native void freeMemory(long l);

简单理解

java process memory = java heap + native memory
java进程内存 = 堆内存 + 本地内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值