jvm学习(一) java虚拟机组成简介

以前记录的笔记,回过头来看竟然有些不理解,这里我会补充一些资料帮助理解

1.java虚拟机的组成

介绍:

jvm是java虚拟机的简称,它是由  类装载子系统 、字节码执行引擎  和 运行时数据区 三部分共同组成其中运行时数据区个版本有一些去区别,后面会进行个版本的对比

我们平时面试被问的多的其实就是运行时数据区

扩展: Math类被加载到到jvm中会经历 加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载 这几个步骤

  • 加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的 java.lang.Class对象,作为方法区这个类的各种数据的访问入口
  • 验证:校验字节码文件的正确性
  • 准备:给类的静态变量分配内存,并赋予默认值
  • 解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接
  • 程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用,下节课会讲到动态链接
  • 初始化:对类的静态变量初始化为指定的值,执行静态代码块

运行时数据区域:

根据《Java 虚拟机规范(Java SE 8版)》规定,Java 虚拟机所管理的内存如下图所示。

程序计数器(PC寄存器)、Java 虚拟机栈、本地方法栈、Java堆、方法区、运行时常量池

具体的jvm结构如下图所示:

  

程序计数器

内存空间小,线程私有。字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成

java虚拟机支持多条线程同时执行,每个线程都有自己独立的程序计数器。如果线程正在执行一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器的值则为 (Undefined)。程序计数器的容量应该至少保存一个returnAddress类型的数据或者一个平台相关的本地指针的值。  - 《Java虚拟机规范(Java SE 8版)》

我的理解:内存空间小(仅仅存放当前线程执行代码的行数),线程私有。因为在多线程的情况,A线程执行到了第4行,这个时候CPU又切换到了B线程执行了3行代码,此时有切换回A线程,通过程序计数器就能继续执行第5行代码(因为刚才已经执行了第4行代码),其他的线程也是类似

Java 虚拟机栈

线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会床创建一个栈帧(Stack Frame)用于存储局部变量表操作数栈动态链接方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。

每一条java虚拟机线程都有自己私有的java虚拟机栈,这个栈与线程同时创建,用于存储栈帧,java虚拟机栈用于储存局部变量与一些还没有计算好的结果。另外,它在方法调用和返回中扮演重要角色。因为除了栈帧的出栈和入栈意外,java虚拟机不会再受其他因素的影响,所以栈帧可以再堆中分配,java虚拟机所使用的内存不需要保证是连续的。   - 《Java虚拟机规范(Java SE 8版)》

StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。
OutOfMemoryError:如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存。
 

我的理解:存放基本数据类型(如果是对象就存放对象的指针地址)  以及它自己特有的结构详情参考下面的栈帧模型

Java 堆

对于绝大多数应用来说,这块区域是 JVM 所管理的内存中最大的一块。线程共享,主要是存放对象实例和数组。内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。

在Java虚拟机中,堆是可供各个线程共享的呢运行时区域,也是供所有类实例和数组对象分配内存的区域。

java堆在虚拟机启动的时候就被创建,它存储了被自动内存管理系统,也就是常说的垃圾收集器所管理的各种对象,这些受管理的对象无需也无法显式的销毁。java堆的容量可以是固定的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。Java堆所使用的内存不需要保证是连续的。     - 《Java虚拟机规范(Java SE 8版)》

java堆的示意图:

OutOfMemoryError:如果堆中没有内存完成实例分配,并且堆也无法再扩展时,抛出该异常。

我的理解:堆也是我们讨论最多的地方,得益于java良好的垃圾收集机制,我们刚开始接触java时并不需要关心他是如何工作的,但是随着技术的不断加深与累积,业务场景的不断复杂,这一块也是我们解决性能优化的重点部分。大名鼎鼎的OutOfMemoryError大部分就出现在这个地方。

方法区

属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

在java虚拟机中,方法区是可供各个线程共享的运行时内存区域。它存储了每一个类的结构信息,例如,运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。

 方法区在虚拟机启动的时候创建,虽然方法区是堆的逻辑组成部分,但是简单的虚拟机实现可以选择在这个区域不实现垃圾收集与压缩。这个版本的java虚拟机规范也不限定实现方法区内存位置和编译代码的管理策略。方法区的容量可以是固定的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。方法区在实际内存空间中可以是不连续的。    - 《Java虚拟机规范(Java SE 8版)》

如果方法区的内存空间不能满足内存分配请求,就会抛出OutOfMemoryError

我的理解:方法区属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。HotSpot虚拟机中,在jdk8之前,方法区在jvm中的实现是永久代,因为永久代垃圾收集行为较少,容易发生OutOfMemoryError,jdk8以及jdk8以后,方法区在jvm中的实现是元空间,元空间不是堆的一部分,它是本地内存,可以调整元空间的大小。与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。

运行时常量池

属于方法区一部分,用于存放编译期生成的各种字面量和符号引用。编译器和运行期(String 的 intern() )都可以将常量放入池中。如果申请的内存超过了方法区的所能提供的最大值抛出 OutOfMemoryError。

我的理解:常量  + 静态变量(包括静态对象,跟栈类似,存放的是对象的指针地址)  +    类信息

拓展:直接内存

非虚拟机运行时数据区的部分

在 JDK 1.4 中新加入 NIO (New Input/Output) 类,引入了一种基于通道(Channel)和缓存(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。可以避免在 Java 堆和 Native 堆中来回的数据耗时操作。
OutOfMemoryError:会受到本机内存限制,如果内存区域总和大于物理内存限制从而导致动态扩展时出现该异常。

 2.代码

public class  Math{

    public static void main(String[] args){

        Math math = new Math();
        int result = math.compute();
        System.out.println("result = " + result);
    }

    public int compute(){
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;

    }

}

此时jvm的栈帧模型如下图所示:

局部变量表

存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)

我的理解:局部变量保存的地方 比如compute方法里面的局部变量  a  b c

操作数栈:变量执行运算的地方   会将数据从局部变量表或者实例的字段复制常量或者变量的值道操作数栈中  比如 a = 1 或者 int c = (a + b) * 10等 然后会将计算结果出栈保存到局部变量表的局部变量中

方法出口:记录方法返回时执行的位置  比如  int result = math.compute() 会进入compute方法的栈帧中,当compute方法返回时,通过方法出口就能知道下一个是执行 System.out.println("result = " + result)

 本地方法栈

区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会有 StackOverflowError 和 OutOfMemoryError 异常。

我的理解:本地方法栈服务的对象是JVM执行的native方法(c/c++等),其就是一个java调用非java代码的接口,作用是与操作系统和外部环境交互

 使用javap查看JVM指令

如下  可对照 JVM指令手册 查看具体含义

Compiled from "Math.java"
public class Math {
  public Math();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Math
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method compute:()I
      12: istore_2
      13: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      16: new           #6                  // class java/lang/StringBuilder
      19: dup
      20: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      23: ldc           #8                  // String result =
      25: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      28: iload_2
      29: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      32: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      35: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      38: return

  public int compute();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        10
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn
}

整体的图如下

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值