JVM的面试考点

JVM内存划分

1.堆,整个内存区域中,内存最大的区域,放的都是new出来的对象,new+类名这一部分存放在堆中,

而这个scanner是一个临时变量,这个scanner的地址存放在栈上,scanner里面存放的值是new+类名这个对象的首地址

2.栈,分为JVM虚拟机栈(Java代码),和本地方法栈(C++),这个栈包含了方法的调用关系

3.元数据区(以前叫做方法区),放的是类对象,代码中的每个类,在JVM上运行的时候,都会有对应的类对象,Text.class还存放了方法相关的信息,类有一些方法,每个方法都代表了一系列的"指令集合"(JVM指令集合)

4.程序计数器,是内存区域中最小的一个区域,只需要保存,当前要执行的下一条指令(JVM字节码)的地址,这个地址就是元数据区里面的一个地址,JVM的PC保存的地址是JVM字节码的地址

关于内存中的地址划分

内存存放基本原则:1)局部变量,堆上

                2)成员变量,堆上

                3)静态成员变量 方法区/元数据区

上述四个区域中,堆和元数据区,是整个进程只有一份,栈和程序计数器是每个线程都有,多个线程同享一份数据,每个线程的局部变量,不是共享的,每个线程有自己的一份

类加载的过程

当前写的Java代码,是.java文件(硬盘),一个Java进程要跑起来,需要执行cpu指令,通过字节码让JVM翻译出来,就需要把.java文件变为.class文件(硬盘),再加载到内存上,得到类对象

重要过程

1.加载:在硬盘上,找到对应的.class文件,读取文件内容

2.验证:检查.class里的内容,是否符合要求,把读取出来的内容,往这个格式里套,看能不能套进去

u4是unsigned int 无符号的int u2是unsigned short 无符号的short

3.准备:给类对象,分配内存空间(在元数据区中),类加载最终要得到的就是类对象,会先把这个空间里的数据全填为0

4.解析:针对字符串常量进行初始化,把刚才.class文件中的常量内容取出来,放到元数据区

5.初始化:针对类对象中的各部分进行初始化(不是针对对象初始化,与构造方法无关)

双亲委派模型

这是一个类加载的机制,根据代码中写的"全限定类名"(包名+类名,例如Java.lang.String)找到对应的.class文件

这个模型描述了JVM加载.class文件过程中,找文件的过程,这个模型中内置了三个类加载器,在JVM中包含了一个特定的模块/类,这个类负责完成后续类加载的工作

JVM中内置了三个类加载器(负责加载不同的类)

1.BootStrapClassLoader,负责加载标准库的类,这个类是Java官方给出的"标准类",

2.ExtentionClassLoader,负责加载JVM扩展库的类

3.ApplicationClassLoader,负责加载第三方库的类和你自己写代码的类

三者之间的关系

此处的父子关系,不是通过类的继承表示的,而是通过类加载器中有一个"parent"这样的字段,指向自己父亲的地址,类似于二叉树的三叉实现

工作过程如下:例如给定一个全限定类名,Java.Test,此时加载过程如下

1.工作从ApplicationClassLoader开始进行,这个类加载器并不会立即从第三方库/自己写的代码开始搜索,而是交给自己的父亲ExtentionClassLoader去处理

2.工作就到了.ExtentionClassLoader,这个类加载器也不会立即从JVM扩展库开始搜索,而是交给自己的父亲BootStrapClassLoader去处理

3.工作就到了BootStrapClassLoader,这个类加载器,也不会立即从标准库中开始搜索,而是继续交给自己的父亲,由于自己的父亲为null,只能自己来处理,BootStrapClassLoader尝试在标准库的路径上开始搜索,如果这个类找到了,搜索过程完成,然后打开文件进行后续操作,如果没找到则交给自己的儿子来进行处理,

4.工作回到了.ExtentionClassLoader这个类加载器尝试在JVM扩展库的路径上开始搜索,如果这个类找到了,搜索过程完成,然后打开文件进行后续操作,如果没找到则交给自己的儿子来进行处理,

5.工作回到了ApplicationClassLoader,这个类加载器尝试在第三方库/自己写的代码中的路径上开始搜索,如果这个类找到了,搜索过程完成,然后打开文件进行后续操作,如果没找到则交给自己的儿子来进行处理,由于它的儿子为null,所以会抛出一个异常ClassNotFoundException

上述工作流程,主要应对这个场景,如果自己写的一个类和标准库/扩展库中的类冲突了,此时JVM就会确保标准库/扩展库的类加载成功,

类加载器并非只有三个,还可以手动写更多的类加载器,添加到中间

JVM的垃圾回收机制GC

垃圾回收机制,是Java提供的对于自动回收的机制,自动回收相对于C++的手动回收来命名的,C++回收需要手动free函数,可能会遗漏这个操作,

GC需要消耗额外的系统资源,而且存在非常影响效率的"STW"(stop the world)问题,GC回收的是内存,更准确的说是"对象",回收的是堆上的内存

1)程序计数器(不需要额外回收,线程销毁,自然就回收了)

2)栈(不需要额外回收,线程销毁,自然回收)

3)元数据区(一般也不需要,都是加载类,很少有"卸载类")

4)堆(GC回收的主力军)

一定是一次回收一个完整的对象,把对象中的成员全都回收

JAVAGC机制有一个方法,GC机制有两个方法

GC的主要流程

1.找到谁是垃圾,不被继续使用的对象

使用对象都是通过引用的方式来使用,如果没有引用指向这个对象,意味着这个对象注定无法在代码被使用,被视为垃圾了,对于JVM来说不是实时的,JVM需要一定的时间周期

如何判断某个对象是否有引用指向呢?

1)引用计数(不是JVM的方案,是Python和PHP的方案)

当引用计数为0时,这个对象就是垃圾了

缺陷如下:

               1)消耗额外的存储空间:如果对象的空间比较大,浪费的空间很小,但是如果对象的空间比较小,浪费的空间就会特别大了

               2)存在"循环引用"的问题

2)可达性分析(是JVM采取的方案)

解决了空间问题,也解决了循环引用的问题,但是时间上效率变慢,通过"遍历",JVM把对象之间的引用关系,理解成一个"树形结构",JVM就会不停的遍历这个结构,把所有能够访问到的对象标记成"可达",剩下的就是"不可达"

2.释放对应的内存

由于可达性分析,需要消耗一定的时间,因此,JAVA垃圾回收,没法做到实时性,会周期性进行扫描(JVM提供了一组专门负责GC的线程,不停的进行扫描工作)

清理垃圾的策略:

        1)标记-清除,直接把视为垃圾的对象对应的内存给释放掉,这样的做法会造成内存碎片化,后续很难申请到连续的空间,申请内存都是需要连续的

        2)复制算法

        3)标记-整理

上述这三个方法,都是铺垫,JVM中实际的方案,是综合上述的方案,更复杂的策略,分代回收(分情况讨论,根据不同的场景/特点选择合适的方案),根据对象的年龄(GC有一组线程,进行周期性扫描,没有成为垃圾的对象每进过一轮年龄+1)

Eden是伊甸区,S0 S1是新生区/幸存区

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值