一、JVM的概念
自学使用题目,`还在完善中,目前的初稿还不算!!我还没背^^`,嘿嘿!!!
1、JVM的具体作用
jvm是java虚拟机Java Virtual Machine的缩写。
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-J1kl1bzW-1657075259410)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_12,color_FFFFFF,t_70,g_se,x_16.jpeg)]
虚拟机是通过模拟实际计算机上的各种计算机功能来实现的抽象计算机。Java虚拟机有自己完善的硬体结构,如处理器、堆栈、寄存器等,并有相应的指令系统。Java虚拟机屏蔽了与特定操作系统平台相关的信息,使Java只需生成程序Java目标代码(字节码)在虚拟机上运行,可在多个平台上运行,无需修改。简单来说JVM用于分析和操作Java程序的。
java跨平台运行的主要原因是java同时,虚拟机jvm可以用来屏蔽系统差异,发明一些新语言jvm完成。这样就达到了到处编译的特点。
2、JVM划分中内存区域
要想知道JVM首先要知道中内存区域的划分。JVM内存区域从哪里来,JVM中的内存是JVM在操作系统中申请一块内存,并将该内存划分为一个区域。
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-guEKxMiG-1657075259412)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_15,color_FFFFFF,t_70,g_se,x_16.webp)]
堆(运行时常量池) | new的对象就会放在堆中 |
---|---|
方法区 | 加载好的类放在方法区,静态成员 |
栈(JVM本地方法栈) | 局部变量 |
程序计数器 | 存储地址描述了当前线程下一步要执行的指令在哪里 |
**注意:**一个过程中有多个线程,每个线程都有自己的栈和程序计数器,一个过程中共用一个堆和一个方法区。
判断变量是堆放还是堆放,与变量是基本类型还是引用类型无关,是局部变量、静态变量还是成员变量。
3.常见的面试问题
具体值存储在基础数据类型中对应的内存空间中
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-5RJNZrpV-1657075259413)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16.png)]
引用数据类型中相应的内存空间存储new对象的地址
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-U9q9JRLh-1657075259414)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144037183.png)]
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-y6AM1zaD-1657075259415)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144037184.png)]
引用或存储地址,new对象是本体。
成员变量堆积
栈上有局部变量
方法区内静态变量
public class Test01 {
///成员变量 Test01 t1=new Test01(); ///静态变量 static Test01 t2=new Test01/span>(); public static void main(String[] args) {
//局部变量 Test01 t3=new Test01(); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-suEESvcl-1657075259415)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038185.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4qM9xnMP-1657075259416)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038186.png)]
虽然这几个变量a,但是他们是处于不同栈帧中的变量。
普通方法中有this(和实例相关),静态方法中没有this(和类相关)普通方法也叫实例方法,静态方法也叫类方法。
二、动态内存管理器(GC)
1、GC的具体作用
GC是垃圾回收机制,java中申请的内存可以被垃圾回收装置进行回收,GC可以一定程度的避免内存泄漏,但是会引入一些额外的开销。
2、GC回收的特点
GC中主要回收的是中的内存,栈中内存的释放要等到线程结束或者是栈帧被销毁,而程序计数器中存储的是地址不需要进行释放。
对于GC中回收的基本单位不是字节而是
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kT2HSOIA-1657075259416)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038187.png)]
GC回收的一般是已经不使用的位置的内存
3、标记
1)引用计数法
每个对象都会分配一个专门的计数器变量当有一个新的引用指向这个变量的时候计数器就会加一,当有一个引用不指向这个变量计数器就会减一,当引用计数器为0时就会让这个对象标记回收。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LMJhSE17-1657075259417)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038188.png)]
但是这就引用出问题不能解决:
public class Test01 {
public static void main(String[] args) {
Test a=new Test();
Test b=new Test();
a.t=b;
b.t=a;
a=null;
b=null;
}
}
class Test{
Test t=null;
}
当想要使用对象a,找到a的引用,这个引用在对象b中,想找对象b的引用在a中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6iI5iDqP-1657075259417)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038189.png)]
2)可达性分析
代码中的对象之间是有一定的关联的,他们构成的一个“有向图”,然后我们去遍历这个“有向图”如果对象遍历的到就不是垃圾,反之就是垃圾。
1、每个栈中栈帧的局部变量表
2、常量池中引用的对象
3、方法区中静态变量引用的对象
遍历的时候不是像二叉树一样从一个地方开始遍历,而是从多个地方遍历,这样的我们统称为GCRoot。
3)方法区类对象的回收规则
1、该类的所有实例都已经被回收
2、加载该类的ClassLoader也已经被回收了
3、该类对象没有在代码中使用
4、引用的类型
**强引用:**可以找到对象也能决定对象的生死
**软引用:**可以找到对象也能在一定程度上决定对象的生死
**弱引用:**可以找到对象但是不能决定对象的生死
**虚引用:**不能找到对象也能不能决定对象的生死
5、回收
1)标记清除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JgWAKNzv-1657075259418)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038190.png)]
标记清除指的是直接释放将标记的区域中的内存释放
2)标记复制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-shyDOEhx-1657075259418)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038191.png)]
将内存划分为两个区域直接拷贝不是垃圾的对象放到另一个区域
3)标记整理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DOR90JIx-1657075259419)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038192.png)]
类似于数组删除数据
6、分代回收
我们可以通过分代回收的方法来更好的清理内存
我们把堆中的内存分为新生代(伊甸区和生存区)和老年代
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WaVe5iJN-1657075259419)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038193.png)]
对象诞生于伊甸区,新对象的内存就是新时代中的内存
当经历过第一轮GC扫描时就会把标记的对象干掉,没有被干掉的就会被拷贝到生存区01
进入生存区中的对象也会经过扫描然后拷贝到生存区02(生存区中的对象是从另外一个生存区和伊甸区过来的)
当对象在生存区经历了若干次拷贝之后没有被回收就说明这个对象存活的时间比较长就会被拷贝到老年代
老年代中的对象也要经过GC扫描,由于老年代中的对象存活的时间比较长,扫描老年代中的周期会比较长
7、垃圾回收器的介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bjOcebKb-1657075259420)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038194.webp)]
垃圾回收器中都是做两件事情,当进行回收的时候应用线程就会停止工作STW。
三、类加载器
1、类加载的基本过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AAYtZhDC-1657075259421)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038195.png)]
我们根据类名找到文件,并且读取文件构造解析,将内容读取到内存中去,并且构造相应的类对象,这个过程叫做。
如果这个类还有其他相关联的类,就会将其他的依赖内容引入,这个过程叫做。
初始静态成员并且执行静态代码块,这个过程叫做。
2、什么时候触发类加载
class Test{
static {
System.out.println("Test.static{}");
}
{
System.out.println("Test{}");
}
public Test() {
System.out.println("Test构造方法");
}
}
class Test0 extends Test{
static {
System.out.println("Test0.static{}");
}
{
System.out.println("Test0{}");
}
public Test0() {
System.out.println("Test0构造方法");
}
}
public class Test01 extends Test0{
public static void main(String[] args) {
System.out.println("开始");
new Test0();
new Test0();
System.out.println("结束");
}
}
执行静态代码块——执行代码块——执行构造方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J339Zcl3-1657075259421)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_16,color_FFFFFF,t_70,g_se,x_16.png)]
3、常见的类加载器
内置的三个加载器 | 加载内容 | 位置 |
---|---|---|
加载标准库中的类(String ArrayList…) | ||
加载一个特殊的类 | ||
加载应用程序自己写的类 |
4、双亲委派模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LZrviCyc-1657075259422)(…/…/…/images/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbm93IGp1c3QgZG8gaXQ=,size_20,color_FFFFFF,t_70,g_se,x_16-1657075144038196.png)]
当一个类开始加载时,会先从AppClassLoader开始,但是它不会立刻查找会先交给自己的父类,ExtClassLoader也会交给自己的父类,然后BootstrapClassLoader拿到类名之后在rt.jar中开始查找,找到就返回,没找到就会在ExtClassLoader中的ext目录中开始查找,找到就返回,没有找到就会在AppClassLoader中的CLASS——PATH环境变量中、-cp指定目录中、当前目录中三个地方去查找,如果找到就加载,没有找到就抛出一个ClassNotFoundException异常。
**注意:**自己写的类加载器是可以违背双亲委派模型的(Tomcat中的类加载器就没有遵守双亲委派模型)