JVM---1

1. JVM 概念

  • JVM(Java Virtual Machine)的简称,java虚拟机。
  • 虚拟机:指通过软件模拟的具有完整硬件功能、运行在一个完全隔离的环境中的完整计算机系统。
  • JVM:被裁剪过的虚拟机,执行字节码指令集的软件。
  • Java代码——Java进程——jvm之间的关系
    在这里插入图片描述
  • 类加载:
    如果一个类没有发生类加载,要限制性类加载。
    类加载的时机:
    1. new关键字实例化对象的时候;
    2. 读,写静态变量
    3. 调用静态方法的时候;
    4. 父类的类加载:子类要初始化类加载,如果子类还没有类加载,则先执行父类的类加载。
    5. 主函数的类:java类启动入口类
    加载做的事情:
    1. 把字节码(二进制数据)加载到java进程的方法区(类型的信息,方法的信息等等)
    2. 在堆中,生成一个Class类对象。作为方法区类信息(代码数据)访问的入口。
    • Java 进程运行起来了,做了什么
    1. 初始化VM的参数
    2. 创建一个java虚拟机,并启动
    3. 入口类类加载,java虚拟机执行main方法
    4. 启动一些守护线程。(GC线程)等等
  • java 进程——线程——申请系统调度cpu执行指令
  • 启动线程:申请系统调度CPU执行线程中的代码执行指令。
  • Java虚拟机:包含解释器,JIT即时编译器
    在这里插入图片描述
    关于JVM
  1. 定制过的裁剪了部分功能的虚拟机,模拟硬件执行(将字节码翻译成机器码)字节码指令;
  2. Java进程启动时,就会创建一个Java虚拟机,解释执行字节码指令。最终是申请系统调度CPU执行机器码。
  3. 不同虚拟机实现,可以运行符合字节码规范的代码(kotlin,scala,groovy),基于自己独有的编译器,编译为符合字节码文件,使用符合Java虚拟机规范,自己定制的java虚拟机来运行。(解释翻译为机器码)

2.Java内存区域

2.1 运行时数据区域

  • 线程私有区域:程序计数器,Java虚拟机栈,本地方法栈
  • 线程共享区域:Java堆,方法区,运行时常量池。
  • 内存溢出:创建变量/对象/类加载,需要先在对应的内存区域,分配一块内存空间,如果该区域内存不足,需要执行gc(垃圾回收),如果gc以后,内存还不够用,就会出现内存溢出(OOM)
  • 内存溢出导致的结果:严重的情况下,整个java进程都会挂掉;
  • 内存溢出的解决方案:
    1. 优化代码,(空间复杂度):复用变量等等。
    2. Java进程启动时,加大对应内存区域的空间。
    3. 如果系统内存不足满足第2个条件,还可以加大系统内存。
      在这里插入图片描述

2.2 程序计数器(线程私有)

  • 程序计数器是一块比较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。(目的:线程切换出去后要恢复时下个指令从哪个地方开始执行)
  • 程序计数器内存区域是唯一一个在JVM规范中没有规定任何OOM的区域。
  • 线程私有:由于JVM的多线程是通过线程轮流切换并分配处理器执行时间来实现,因此在任何一个确定的时刻,一个处理器(多核处理器则指的是一个内核)都只会执行一条线程中的指令,因此为了切换线程后能恢复到正确的执行位置,每条线程都需要独立的程序计数器,各条线程之间计数器互补影响,独立存储,我们把类似这类区域称之为“线程私有”的内存。

2.3 Java虚拟机栈(线程私有)

  • 虚拟机栈描述的是Java方法执行的内存模型:每个方法执行的同时都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用开始直至完成的过程,就对应一个战帧在虚拟机中入栈和出栈的过程,Java虚拟机栈的生命周期和线程相同。(线程启动,虚拟机栈就会创建,线程销毁,虚拟机栈也销毁)。
  • 局部变量表:存放了编译器可知的各种基本数据类型(8大基本数据类型),对象引用。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在执行期间不会改变局部变量表的大小。
class Person{
    String name;
    public Person(String name){
        this.name = name;
    }
}

public class VMStackTest {

    public static void main(String[] args) {
        int m = 1;
        int n = 2;
        swap1(m,n);
        System.out.printf("main: m=%s,n=%s%n",m,n);
    }

    private static void swap1(int m, int n) {
        int tmp = m;
        m = n;
        n = tmp;
        Person person1 = new Person("p1");
        Person person2 = new Person("p2");
        swap2(person1,person2);
        System.out.printf("swap1 :p1=%s,p2=%s%n",person1.name, person2.name);
    }

    private static void swap2(Person p1, Person p2) {
        Person tmp = p1;
        p1 = p2;
        p2 = tmp;
        System.out.printf("swap2:p1= %s, p2= %s %n",p1.name,p2.name);
    }
}

在这里插入图片描述

  • 基础数据类型是传值,引用的传引用地址的值。
  • 虚拟机栈不需要垃圾回收,出栈自动销毁栈帧,所以不需要栈帧。
  • 此区域一般会产生下列两种异常:
  1. 如果线程请求的栈的深度大于虚拟机所允许的深度,将会抛出StackOverFlowError异常。(线程调用的方法链太深)
  2. OOM

2.4 本地方法栈(线程私有)

  • 本地方法栈与虚拟机栈的作用完全一样,他俩的区别无非是本地方法栈为虚拟机使用的Native方法服务,而虚拟机栈为JVM执行的java方法服务。基于JNI的技术,调用native本地方法(其他语言提供符合JNI规范的接口)。

2.5 堆(线程共享)

  • Java堆(Java Heap)是JVM所管理的最大内存区域,Java堆是所有线程共享的一块区域,在JVM启动时创建,此内存区域存放的都是对象实例。JVM规范中说到:“所有的对象实例以及数组都要在堆上分配。”线程共享的区域,都是Java进程启动时就创建的。
  • Java堆是垃圾回收管理的主要区域,因此很多时候可以称之为"GC堆",根据JVM规范规定的内容,Java堆可以处于物理上不连续的内存空间。

2.6 方法区(线程共享)

  • 方法区用于存储已被虚拟机加载的类信息,常量(static final修饰的常量)、静态变量(等号左边的变量本身,1.7及以后,是存放在堆里面的),即时编译器编译后的代码等数据。
  • 存在gc:回收常量和类型(卸载)类型卸载的条件是非常苛刻d额,所以方法区的gc频率比较低。

2.7 运行时常量池

  • 运行时常量池是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于编译期生成的各种字面量与符号引用,这部分内容在类加载后存放到方法区的运行池中。
    在这里插入图片描述
  • 其他常量池补充:
    • class文件常量池,编译为class字节码文件中,存在class文件常量池,用于存放字面量和符号引用。
    • 字符串常量池
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值