初级JVM知识

一、解释还是编译

解释:解释表示代码在运行的过程不存在编译的过程,而是读一行执行一行。

编译:编译指的是代码会被编译成机器指令,再由机器去执行。

Java是一名解释与编译都有的一门语言。

​ 存在编译的过程:会把Java源代码编译成class文件,也就是字节码

​ 解释的过程:JVM会识别字节码,通过解释的方式读一行,执行一行的方式运行。

Java的编译器:

​ 前端编译器:Javac指令,把Java源代码编译位字节码。

​ JIT编译器:及时编译器,会把热点class文件编译成机器代码。

​ AOT编译器:能将Java源代码直接编译成机器指令。

二、动态类型还是静态类型

动态类型:变量在声明的时候不确定类型。

​ 变量在编译的时候不确定类型

​ 变量在运行的时候确定类型,在运行的时候可以更改

静态类型:变量在声明的时候就确定了类型。

​ 变量在编译的时候就确定了类型

​ 变量在运行的时候不能更改类型

强类型语言

​ 变量如果被指定了数据类型,那么他只能通过强制类型转换改变数据类型

弱类型语言1

​ 变量的数据类型可以在运行过程中随意更改

Java是一名静态类型的强类型语言

三、实例对象在内存的分配

普通对象和数组对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GVqYGABt-1671110439350)(C:\Users\playclear\AppData\Roaming\Typora\typora-user-images\image-20221204172715140.png)]

举例:

1.Object对象:12(对象头) + 0(数据区域) + 4(对齐) = 16

2.Object数组对象:12(对象头) + 0(数据区域) + 4(数组长度) + 0(对齐)= 16

public class Test {
    private byte[] bytes;
    private int age;

    public Test(byte[] bytes, int age) {
        this.bytes = bytes;
        this.age = age;
    }

    public byte[] getBytes() {
        return bytes;
    }

    public void setBytes(byte[] bytes) {
        this.bytes = bytes;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
        Test test = new Test(new byte[12],12);
        System.out.println(ObjectSizeCalculator.getObjectSize(test));
    }
}

Test对象占多大空间:

分两部分计算

1.Test对象:12(对象头) + 4(bytes的引用) + 4(age)+ 4(对齐) = 24

2.bytes: 12(对象头) + 4(长度) + 12(数据大小) + 4(对齐) = 32

all: 24 + 32 = 56

四、类的加载

加载-连接-初始化-使用-卸载

1.加载:把class文件以二进制流的形式加载到方法区中,并生成Java.lang.Class对象。

2.连接:

​ 验证:对class文件的字节流进行安全性校验(文件格式、元数据、字节码等)

​ 准备:位静态变量赋予初始值,并在方法区为其分配空间

​ 解析:将常量池的符号引用替换为直接引用

3.初始化:收集静态变量的赋值动作和静态代码块然后初始化

五、类加载器

Bootstrap Classloader 启动类加载器(加载JVM需要的类,主要是 <JAVA_HOME>/lib 下的)

Extension Classloader 扩展类加载器 (主要是 <JAVA_HOME>/lib/ext 下的,开发者也可以)

Application Classloader 系统应用类加载器(加载class Path下的)

使用双亲委派模式(这种委派给上一个类加载器的模式是通过代码逻辑实现的,在Application Classloader类加载器中维护了一个parent成员变量,类型是Class loader。基本逻辑是判断有没有父亲,有就交给父亲加载,如果父亲没有加载成功,在由自己去加载)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cB0unrN3-1671110439350)(C:\Users\playclear\AppData\Roaming\Typora\typora-user-images\image-20221205141656210.png)]

六、运行时数据区

在这里插入图片描述

共有:

​ 方法区:存放JVM加载类的信息,是一个存放元数据的区域。

​ 堆:是存放对象实例的区域,从方法区元数据实例后的对象

私有:

​ 虚拟机栈:线程需要执行每个方法需要的存储结构

​ 本地方法栈:存放被native修饰的方法,是由C++编写的

​ 程序计数器:用于记录发生线程切换记录操作进行到了哪步,在解析后的字节码文件中就有序号

七、垃圾回收

1.如何标记?

​ 引用计数法:为对象添加引用计数器,被引用一次+1;引用失效-1,为0则回收。存在循环依赖的问题

​ 根可达算法: 以GC Root为起点,向下开始搜索,被搜索到的就标记。未被搜索到的就会被清除。

​ GC Root有哪些?

​ 1.虚拟机栈中的对象

​ 2.方法区中静态属性引用的对象

​ 3.方法区中常量引用的对象

2.如何删除(垃圾回收算法)?

​ 1.标记-清除

​ 简单粗暴,标记的留下,未标记的删除。

​ 缺点:1.会产生内存碎片。

​ 2.效率低

​ 2.复制算法

​ 开辟两片相同的空间A和B, 先在A空间标记不清除的空间,然后将标记的空间复制到B空间,在清除A空间,如此反复。

​ 缺点:空间换时间

​ 3.标记-整理算法

​ 第一步标记 不清理的空间,第二步将标记的空间向前挪动,第三部清理标记好的边界以外的空间。

​ 缺点:整理耗时

​ 4.分代年龄算法(2,3的结合)

​ 对对象进行年龄分代,分为年轻代和老年代。

​ 年轻代有自己存放的区域,叫做伊甸园和幸存区。在幸存区采用复制算法进行垃圾回收。

​ 老年代存放在老年代中,采用标记整理-算法。

在这里插入图片描述

​ 具体流程:

​ 第一步:一个崭新的0岁的对象被创建准备放入伊甸园,伊甸园没满则成功放入,伊甸园满了就第二步。

​ 第二步: 伊甸园满了就会进行一次MinorGC,幸存的对象放入幸存区的左区域(图示的左边的区域)中,当左区域满了,开始进行复制算法。

​ 当满足以下条件的一个就会被放入老年代区域:

​ 1.每执行一次GC操作,对象的年龄就会+1(该数据存放在对象头中),当年龄等于15的时候(默认15)就会被放入老年代;

​ 2.大对象直接放入老年代

​ 3.如果同一个年龄的对象所占的空间大于幸存区的一半的时候,就会把这个年龄以及大于他们年龄的对象放入老年代。

​ 第三步:当老年代的空间满了之后,就会触发MajorGC,使用的算法是标记-整理算法。

​ 空间分配担保:

​ 在每次MinorGC之前都会进行一次检查,当老年代的剩余可用空间大于所有新生代的对象大小,只有这样才能保证MinorGC是安全的;

​ 原因:如果所有的新生代的对象都将会被放入老年代中,上面的条件不成立就会触发OOM堆内存溢出。

八、垃圾回收器

待更新。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值