Java虚拟机的基本机构

Java虚拟机的基本机构


Java虚拟机的基本结构]![(https://img-blog.csdn.net/20171115104849553?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbHdfempfeXdu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

类加载子系统负责从文件系统或者网络中加载Class信息,加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区还会存放运行时常量池信息,包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)。
Java堆在虚拟机启动的时候建立,它是Java程序最主要的内存工作区域。几乎所有的Java对象实例都存放于Java堆中。堆空间是所有线程共享的,这是一块与Java应用密切相关的内存空间。
Java的NIO库允许Java程序使用直接内存。直接内存是在Java堆外的、直接向系统申请的内存空间。通常访问直接内存的速度会优于Java堆。因此处于性能考虑,读写频繁的场合可能会考虑使用直接内存。由于在Java堆外,直接内存大小不受限于Xmx指定的最大堆大小,但Java堆和直接内存的总和依然受限于操作系统能给出的最大内存。
垃圾回收系统是Java虚拟机的重要组成部分,垃圾回收器可以对方法区,Java堆和直接内存进行回收。其中Java堆是主要工作重点。
每一个Java虚拟机线程都有一个私有的Java栈。一个线程的Java栈在线程创建的时候被创建。Java栈中保存着帧信息,Java栈中保存着局部变量方法参数,同时和Java方法的调用返回密切相关。
本地方法栈和Java栈非常类似,最大的不同在于Java栈用于Java方法的调用,而本地方法栈则用于本地方法的调用。
PC(Program Counter)寄存器也是每个线程私有的空间,Java虚拟机会为每一个Java线程创建PC寄存器。在任意时刻,一个Java线程总是在执行一个方法,这个正在被执行的方法称为当前方法。如果当前方法不是本地方法,PC寄存器就会指向当前正在被执行的指令,如果当前方法是本地方法,那么PC寄存器的值就是undefined。
执行引擎是Java虚拟机的最核心组件之一,它负责执行虚拟机的字节码。现代虚拟机为了提高执行效率,会使用即时编译技术将方法编译成机器码后再执行。

Java堆的结构


堆空间的一般结构]![这里写图片描述

根据垃圾回收机制的不同,Java堆有可能拥有不同的结构。最为常见的一种构成是将整个Java堆分为新生代和老年代。其中新生代存放新生对象或者年龄不大的对象,老年代则存放老年对象。新生代有可能分为eden区、s0区、s1区,s0和s1也被称为from区和to区域,它们是两块大小相等、可以互换角色的内存空间。
在绝大多数情况下,对象首先分配在eden区,在一次新生代回收后,如果对象还存活,则会进入s0或者s1,之后,每进行一次新生代回收,对象如果存活,他的年龄就会+1。当对象的年龄达到一定条件后,就会被认为是老年对象,进入老年代。

Java堆、方法区和Java栈之间的关系

public class SimpleHeap {
    private int id ;

    public SimpleHeap(int id) {
        this.id = id;
    }
    public void show() {
        System.out.println("My ID is "+id);
    }

    public static void main(String[] args) {
        SimpleHeap s1 = new SimpleHeap(1);
        SimpleHeap s2 = new SimpleHeap(2);
        s1.show();
        s2.show();
    }
}

这里写图片描述

Java栈


Java栈是一块线程私有的内存空间。如果说Java堆和程序数据密切相关,那么Java栈就是和线程执行密切相关的。线程执行的基本行为是函数调用,每次函数调用的数据都是通过Java栈传递的。
Java栈是一块先进后出的数据结构,只支持出栈和入栈两种操作。。在Java栈中保存的主要内容为栈帧。函数调用,对应的栈帧入栈,调用结束,对应的栈帧被弹出Java栈。当前正在执行的函数所对应的帧就是当前帧(位于栈顶),它保存这当前函数的局部变量、中间运算结果等数据。
在一个栈帧中,至少包含局部变量表、操作数栈和帧数据区几个部分。

局部变量表

局部变量表是栈帧的重要组成部分之一。它用于保存函数的参数以及局部变量。当函数调用结束后,随着函数栈帧的销毁,局部变量表也会随之销毁。

操作数栈

操作数栈也是栈帧中重要的内容之一,他主要存在于保存计算过程的中间结果,同时作为计算过程中变量的存储空间。

帧数据区

除了局部变量表和操作数栈外,Java栈帧还需要一些数据来支持常量池解析、正常方法返回和异常处理等。大部分Java字节码指令需要进行常量池访问,在帧数据区中保存着访问常量池的指针,方便程序访问常量池。
此外,当函数返回或者出现异常时,虚拟机必须恢复调用者函数的栈帧,并让调用者函数继续执行下去。对于异常处理,虚拟机必须有一个异常处理表,方便在发生异常的时候找到处理异常的代码,因此异常处理表也是帧数据区中重要的一部分。

栈上分配

栈上分配是Java虚拟机提供的一项优化技术,他的基本思想是,对于那些线程私有的对象(这里指不可能被其他线程访问的对象),可以将它们打散分配在栈上,而不是分配在堆上。分配在栈上的好处是可以在函数调用结束后自行销毁,而不需要垃圾回收器的介入,从而提高系统的性能
栈上分配的一个技术基础就是进行逃逸分析,逃逸分析的目的是判断对象的作用域是否有可能逃逸出函数体。如下是一个逃逸的对象

private static User u;
public static void alloc(){
    u=new User();
    u.id = 5;
    u.name = "game";
}

对象User是类的成员变量,该字段有可能被任何线程访问。因此属于逃逸对象。以下是一个非逃逸对象:

public static void alloc(){
    User u=new User();
    u.id = 5;
    u.name = "game";
}

这种情况虚拟机就有可能将User分配在栈上,而不在堆上。

方法区

和Java堆一样,方法区是一块所有线程共享的内存区域。它用于保存系统的类信息,比如类的字段、方法、常量池等。方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值