JVM--内存区域

在这里插入图片描述

1. 方法区

这个方法区是在JDK 1.8以前的版本里,代表JVM中的一块区域。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,运行时常量池(Runtime Constant Pool)也是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。在JDK 1.8以后,这块区域的名字改叫 Metaspace(元数据空间)。

在这里插入图片描述

2.执行代码指令用的程序计数器

我们写的代码都是带 .java 后缀的源文件,需要通过编译器编译成 .class 后缀的字节码文件,字节码才是计算器理解的一种语言。字节码文件存放的各种“字节码指令”,它对应了一条一条的机器指令,计算机只有读到这种机器码指令,才知道具体应该要干什么。

所以当JVM加载类信息到内存之后,实际就会使用自己的字节码执行引擎,去执行我们写的代码编译出来的代码指令,如下图:

在这里插入图片描述

在执行字节码指令的时候,JVM就需要一个特殊的内存区域用来记录当前执行的字节码指令的位置,这就是“程序计数器”,用于记录目前执行到了哪一条字节码指令。

在这里插入图片描述

实际项目过程中,我们都是开启多个线程并发执行不同的代码,所以就会有多个线程来并发的执行不同的代码指令,因此每个线程都会有自己的一个程序计数器,专门记录当前这个线程目前执行到了哪一条字节码指令了。

在这里插入图片描述

3.虚拟机栈

与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame [1] )用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

比如main线程执行了main()方法,那么就会给这个main()方法创建一个栈帧,压入main线程的Java虚拟机栈
同时在main()方法的栈帧里,会存放对应的 student 局部变量。然后调用了 studentstudy()方法,就会为 study()方法创建一个栈帧压入线程自己的Java虚拟机栈里面去。然后在栈帧的局部变量表里就会有 week 这个局部变量。接着如果 study方法调用了另外一个 getFinish() 方法 ,这个方法里也有自己的栈帧和局部变量。

public class ExamApp {

    public static void main(String[] args){
        System.out.println("开始执行main方法==");
        Student student  = new Student();
        student.study();
        System.out.println("结束执行main方法==");
    }
}
public class Student extends Person{public void study(){
        int week = 0;
        if(getFinish()){
            System.out.println("finish");
        }
    }

    private boolean getFinish(){
        boolean isFinish = true;
        return isFinish;
    }

}

在这里插入图片描述

如果 getFinish() 方法执行完毕了,就会把 getFinish 方法对应的栈帧从Java虚拟机栈里给出栈;如果 study() 方法也执行完毕了,就会把 study 方法也从Java虚拟机栈里出栈。上述就是JVM中的“Java虚拟机栈”这个组件的作用:调用执行任何方法时,都会给方法创建栈帧然后入栈。

在这里插入图片描述

4.堆内存

对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

在这里插入图片描述

如以下代码,ExamApp 在执行main方法的时候会 new Student() 创建一个Student对象,这个对象就是放在堆内存里面的,对象里面包含对象实例的数据,如name,age 。在线程执行main方法代码的时候,就会在main方法对应的栈帧的局部变量表里,让一个引用类型为student 局部变量来存放 Student 对象的地址。

public class ExamApp {

    public static void main(String[] args){
        System.out.println("开始执行main方法==");
        Student student  = new Student();
        student.study();
        System.out.println("结束执行main方法==");
    }
}
public class Student extends Person{
    private String name;
    private Integer age;

    public void study(){
        int week = 0;
        if(getFinish()){
            System.out.println("finish");
        }
    }

    private boolean getFinish(){
        boolean isFinish = true;
        return isFinish;
    }

}

在这里插入图片描述

5.本地方法栈

本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

6.直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现,所以我们放到这里一起讲解。
在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。显然,本机直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,肯定还是会受到本机总内存(包括RAM以及SWAP区或者分页文件)大小以及处理器寻址空间的限制。服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zheng45

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值