【JVM】Java内存模型

java内存模型

  • 1. java虚拟机的生命周期
  • 2. java虚拟机与main方法的关系
  • 3. java的虚拟机种有两种线程
  • 4. JVM内存分哪几个区,每个区的作用是什么?
    • 4.1 方法区(线程共享)
    • 4.2 堆(线程共享)
      • 4.2.1 堆内存结构
    • 4.3 栈
      • 4.3.1 栈运行原理(先进后出)
      • 4.3.2 帧的组成
    • 4.4 本地方法栈
    • 4.5 程序计数器
    • 4.6 方法区与堆的区别
  • 5. jvm各模块的生命周期总结

1. java虚拟机的生命周期

生命周期起点是当一个java应用main函数启动时虚拟机也同时被启动,而只有当在虚拟机实例中的所有非守护进程都结束时,java虚拟机实例才结束生命。

2. java虚拟机与main方法的关系

main函数就是一个java应用的入口,main函数被执行时,java虚拟机就启动了。启动了几个main函数就启动了几个java应用,同时也启动了几个java的虚拟机。

3. java的虚拟机种有两种线程

一种叫守护线程,一种叫非守护线程(也叫普通线程),main函数就是个非守护线程,虚拟机的gc就是一个守护线程。java的虚拟机中,只要有任何非守护线程还没有结束,java虚拟机的实例都不会退出,所以即使main函数这个非守护线程退出,但是由于在main函数中启动的匿名线程也是非守护线程,它还没有结束,所以jvm没办法退出

public class MianAndThread{
    public static void main( String args[]){
        new Thread(new Runnable(){
                    @override
                    public void run(){
                        Thread.currendThread.sleep(5000s);
                        System.out.println("睡了5s后打印,这是出main之外的非守护线程,这个推出后这个引用结束,jvm声明周期结束。任务管理的java/javaw.exe进程结束"
                    }
        }
        System.out.println("mian线程直接打印,mian线程结束,电脑任务管理器的java/javaw.exe进程并没有结束。")
    }   
} 

4. JVM内存分哪几个区,每个区的作用是什么?

4.1 方法区(线程共享)

  1. 主要用来存储已被虚拟机加载的类的信息、常量、静态变量、运行时常量池(该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中)
  2. 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载

4.2 堆(线程共享)

在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此java堆的空间也是最大的。如果java堆空间不足了,程序会抛出OutOfMemoryError异常。
该区域经常发生垃圾回收操作。

4.2.1 堆内存结构

在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常分为下面三部分:
新生代内存(Young Generation)
老生代(Old Generation)
永久代(Permanent Generation)
下图所示的 Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。
【JVM】Java内存模型_第1张图片
JDK 8 版本之后 PermGen(永久) 已被 Metaspace(元空间) 取代,元空间使用的是直接内存。

4.3 栈

java栈是每个线程私有的区域,它的生命周期与线程相同。如果java栈空间不足了,程序会抛出StackOverflowError异常,想一想什么情况下会容易产生这个错误,对,递归,递归如果深度很深,就会执行大量的方法,方法越多java栈的占用空间越大
每个帧代表一个方法,Java方法有两种返回方式,return和抛出异常,两种方式都会导致该方法对应的帧出栈和释放内存。

4.3.1 栈运行原理(先进后出)

栈由一个个栈帧组成。
每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。
遵循先进后出原则。

4.3.2 帧的组成

局部变量表、操作数栈、动态链接、方法返回地址。
局部变量表:主要存放了编译期可知的各种数据类型、对象引用(是一个指向对象起始地址的引用指针)。
操作数栈 :用于存放方法执行过程中产生的中间计算结果。另外,计算过程中产生的临时变量也会放在操作数栈中。
动态链接:主要服务一个方法需要调用其他方法的场景。在 Java 源文件被编译成字节码文件时,所有的变量和方法引用都作为符号引用保存在 Class 文件的常量池里。当一个方法要调用其他方法,需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。动态链接的作用就是为了将符号引用转换为调用方法的直接引用。
【JVM】Java内存模型_第2张图片

4.4 本地方法栈

本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。

4.5 程序计数器

每个线程都有一个程序计算器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。
程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

4.6 方法区与堆的区别

方法区存放了类的信息,有类的静态变量、final类型变量、field自动信息、方法信息,处理逻辑的指令集,我们仔细想想一个类里面也就这些东西,而堆中存放是对象和数组,咋一看好像方法区跟堆的作用是一样的。其实呢,1,这里就关系到我们平时说的对象是类的实例,是不是有点恍然大悟了?这里的对应关系就是 “方法区–类” “堆–对象”,以“人”为例就是,堆里面放的是你这个“实实在在的人,有血有肉的”,而方法区中存放的是描述你的文字信息,如“你的名字,身高,体重,还有你的行为,如吃饭,走路等”。2,再者我们从另一个角度理解,就是从前我们得知方法区中的类是唯一的,同步的。但是我们在代码中往往同一个类会new几次,也就是有多个实例,既然有多个实例,那么在堆中就会分配多个实例空间内存。

5. jvm各模块的生命周期总结

*启动一个jvm虚拟机程序就是启动了一个进程。启动的同时就在操作系统的堆内存中开辟一块jvm内存区
虚拟机栈、本地方法栈、程序计数器这三个模块是线程私有的,有多少线程就有多少个这三个模块,声明周期跟所属线程的声明周期一致。以程序计数器为例,因为多线程是通过线程轮流切换和分配执行时间来实现,所以当线程切回到正确执行位置,每个线程都有独立的程序技术器,各个线程之间的计数器互不影响,独立存储。
其余是跟JVM虚拟机的生命周期一致。

方法区的内存空间不能满足内存分配需要时,将抛出OutOfMemoryError异常
java栈空间不足了,程序会抛出StackOverflowError异常
java堆空间不足了,程序会抛出OutOfMemoryError异常
程序计数器模块是JVM内存区域唯一不会报outofMemoryError情况的区域

你可能感兴趣的:(面试题2022,jvm,java,开发语言)