JVM内存模型

参考教程:B站狂神说Java

JAVA程序运行过程

一个java项目从完成、运行到结束一共经过一下几步

  • .java文件通过javac编译为.class文件

  • .class文件通过类加载器加载为Class对象并放在方法区中

    (一个class文件即一个类只有一个对应的Class对象,这个Class对象包含了关于这个类的描述信息,相当于该类的模板)

  • 开启主线程(即运行main方法),并将main方法压进java栈中

  • 执行java栈顶的方法(栈帧)

    • 调用了其他方法,将该方法压到栈顶,执行栈顶的方法
    • new了一个对象,在堆中的开辟一片空间存放该对象,并在栈帧中存放该对象在堆中的地址进行引用
    • 方法结束,将栈帧弹出java栈
  • 程序运行结束

当然,这只是一个简单的过程,以便于简单理解JVM的内存结构,下面将详细对每一个区域进行分析

在这里插入图片描述

类加载器

类加载器负责将编译好的class文件加载为Class对象,每个不同的类只有唯一一个Class对象,该对象包含了该类的所有信息,如类中所包含的方法、所实现的接口、该类的类加载器等,相当于一个模板

类加载器一共分为3个,分别为:根类加载器、扩展类加载器和应用程序类加载器

根加载器

顾名思义,是最底层、最根本的类加载器,用于加载rt.jar包下的类,该包下为java中最基本的类,如:java.lang.String、java.util.List等

该加载器用C/C++编写的在打印一下代码时结果为null

System.out.println(String.class.getClassLoader());
//输出
//null

扩展类加载器

该加载器为根加载器的下一级,用于加载jre目录下/lib/ext中所存放的jar包中的类

一下为其中一个jar包下的类,代码输出结果如下

System.out.println(AWTEventMonitor.class.getClassLoader());
System.out.println(AWTEventMonitor.class.getClassLoader().getParent());
//输出
//sun.misc.Launcher$ExtClassLoader@6d6f6e28
//null

应用程序类加载器

该类加载器为扩展类加载器的下一级,是3个类加载器中最下级的一个,用于加载其他所有的自定义类

System.out.println(Test.class.getClassLoader());
System.out.println(Test.class.getClassLoader().getParent());
System.out.println(Test.class.getClassLoader().getParent().getParent());
//输出
//sun.misc.Launcher$AppClassLoader@18b4aac2
//sun.misc.Launcher$ExtClassLoader@4554617c
//null

方法区

方法区中存放了类中所定义的静态变量、常量、Class对象以及运行时常量池

方法区只是一种规范,而他的实现为永久区/元空间

堆中存放的为每个线程在执行中所产生的对象实例,该对象的方法、变量等都会存放在堆中

堆中包含新生区、养老区、永久区,而新生区可细分为新生区、存活区form、存活区to

  • 新生区:对象被新建出来时放在新生区
  • 存活区:在进行一次垃圾回收(GC)后,在新生区中没有被回收的对象,即存活的对象会移到存活区
  • 养老区:在经历15次(JVM默认值)GC后,依然没有被回收的对象会从存活区移到养老区
  • 永久区:方法区的一种实现,存放方法区所要存放的东西
    • jdk1.6及以前:永久区存在,数据存放在堆中,内存与新生区、养老区相连,常量池放在永久区中
    • jdk1.7:永久区逐渐退化,常量池存放在堆中
    • jdk1.8及以后:元空间代替永久区,元空间不再放在堆中,而是独立的内存空间,此时常量池在元空间

垃圾回收算法(GC)

判断算法:

  • 引用计数法

    对每个对象添加一个引用计数的属性,记录该对象正在被引用的计数,当计数为0,即该对象没有被引用时进行回收

  • 可到达算法

    从一系列"GC Roots"对象作为起点,搜索该对象的所有引用链,如果一个对象在每个GC Roots对象的所有引用链中都没有达到,即判断为垃圾进行回收

回收算法:

  • 复制算法

    把经过判断算法后判断所存活下来的对象进行复制,将这些对象复制到一个新的空间进行保存,并把原来的对象与垃圾对象一起回收,释放内存空间

    优点:时间复杂度低,没有内存碎片

    缺点:浪费内存,需要有一片内存为空的内存以便存放复制的对象

  • 标记清除算法

    对存活的对象进行标记,然后回收清除没有被标记的对象

    优点:时间复杂度低

    缺点:容易产生内存碎片

  • 标记压缩算法

    对存活的对象进行标记,然后把这些对象移动到相邻的一片内存中

    优点:没有内存碎片

    缺点:时间复杂度高

总体垃圾回收:

  • GC(轻GC):在新生区内存满了的时候执行

    由于新生区中的对象存活率低,进行GC后可用内存空间大,使用复制算法,将新生区和幸存区form中存活的对象转移到幸存区to,然后将空了的幸存区form改为幸存区to,相对的幸存区to改为幸存区form,保证幸存区to的内存一直为空,以便执行复制算法

  • Full GC(重GC):在新生区及存活区form内存都满了的时候执行

    Full GC会在GC的基础上对养老区的对象进行GC;如果某个幸存区中的对象在多次GC中(默认15次)依然存活下来,将会把该对象转移到养老区

    由于养老区中的对象存活率高,进行GC后可用内存空间小,且Full GC使用执行频率较低,使用标记清除算法和标记压缩算法,先将未标记的对象进行回收清除,然后将存活的对象移动到相邻的一篇内存中,保证没有内存碎片

内存溢出(OOM):

当新生区以及养老区的内存都满了,且Full GC已经无法再清理垃圾时,会爆出OOM内存溢出错误,此时程序将会直接崩溃

线程:

一个线程包含3个区域:java栈、本地方法栈以及程序计数器,这3个区域是每个线程所私有的,而方法区以及堆是被每个线程所共享的

java栈

在java栈中存放了程序所需执行的方法,每个方法为一个栈帧,当程序调用了一个方法时,这个方法的栈帧会入栈,当这个方法执行完毕后,该栈帧会出栈,当栈中没有栈帧时线程运行结束死亡

栈帧包括:

  • 本方法的索引:本方法在栈中的地址

  • 局部变量:方法中定义的临时变量,为基本数据类型

  • 对象引用:存放对象实例在堆中的地址以进行引用

    在进行new操作时,会先在方法区中找到对应的Class对象,并新建一个变量名,然后会通过该对象创建一个实例,该实例放在堆中,而栈帧中存放该实例在堆中的地址

  • 。。。

栈溢出:

当不断调用方法导致栈满时会爆出栈溢出错误,如:递归调用方法而不能退出递归时

本地方法栈

作用与java栈相同,不过该栈中执行的方法为本地方法,而非java方法

java通过native关键字调用本地方法接口(JNI),以调用本地方法库执行其他语言所写的代码,如:Thread类中的start0方法

public synchronized void start() {
    if (this.threadStatus != 0) {
        throw new IllegalThreadStateException();
    } else {
        this.group.add(this);
        boolean var1 = false;
        try {
            this.start0();
            var1 = true;
        } finally {
            try {
                if (!var1) {
                    this.group.threadStartFailed(this);
                }
            } catch (Throwable var8) {
            }
        }
    }
}
private native void start0();

线程启动调用start方法,而start方法的本质是调用使用native关键字修饰的start0方法,该方法调用C/C++的代码对硬件进行操作,从而开启一个新的线程

java中还有很多类似的本地方法,如Object中的hashCode方法

程序计数器

为保证线程在cpu中进行切换后,执行进度能与上次在cpu执行后的进度保持一致,使用程序计数器记录上次所执行命令的位置

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值