JVM的学习

目录

JVM整体组成部分

类加载系统

作用

类什么时候被加载

那么什么时候不会被加载呢?

类加载的流程是什么(了解)

类加载器

双亲委派机制

那为什么要设计双亲委派机制呢?

如何打破双亲委派机制呢?

运行时数据区

程序计数器

虚拟机栈

本地方法栈

方法区

本地方法接口

执行引擎(黑盒)

垃圾回收(重点)

垃圾回收相关算法

垃圾标记阶段算法

垃圾回收阶段算法

垃圾回收器

[CMS(Concurrent Mark Sweep, 并发标记清除)](#CMS(Concurrent Mark Sweep, 并发标记清除))

[G1(Garbage First)回收器](#G1(Garbage First)回收器)


jdk(Java Development Kit) Java开发工具包 JRE+开发工具(编译器(javac),调试器等)

jre(Java Runtime Enviroment)Java运行时环境 JVM+工具类(String 等等)

jvm Java虚拟机 将字节码 解释 / 编译 为机器码执行,也是java可以一次编程,到处运行的核心

JVM整体组成部分

类加载系统:将硬盘里的字节码加载到内存里

运行时数据区:负责存储运行时的各种数据

执行引擎:将字节码编译为机器码执行

本地方法接口:调用非java的方法,比如hashCode(获取对象的地址)

类加载系统

作用

将硬盘中的字节码加载到内存中(运行时数据区)

类什么时候被加载

1.类里面写了main方法,执行main方法时会调用

2.new这个类的对象

3.反射机制调用时

4.调用这个类的静态成员

那么什么时候不会被加载呢?

1.例子:Hello[] hello = new Hello [] 此时类就没有被加载,因为创建的是数组对象,Hello这个类只是作为类型

2.访问类里面的静态常量,比如 final static int a = 10; 直接返回静态常量的值

如果是static int a = 10;这是静态变量,也就是类的静态成员,会加载类

类加载的流程是什么(了解)

加载:以字节流形式读取文件

链接:验证,准备,解析

初始化:主要给静态变量成员赋值

类加载器

类加载器就是加载类的实践者

不同的类是由不同的类加载器加载的

类加载器分为三类:

启动类加载器(引导类加载器):不是用java语言写的,使用c/c++写的,负责加载虚拟机最核心的类库

扩展类加载器:使用Java语言写的

应用程序类加载器:使用Java语言写的,负责加载程序员在项目中写的类(target/class)

双亲委派机制

当传来类加载的任务时,会优先委派给上级的类加载器加载,依次递归,直到传到最上层的启动类加载器,如果上级类加载器找到类时,成功返回,如果没有,则委派给下一级类加载器,如果找到了,成功返回, 如果没有,则报ClassNotFoundException。

那为什么要设计双亲委派机制呢?

为了安全,避免了自己定义 的类,替换了系统中的核心类

有一个作用是,当我们自己构造了一个java.lang.String类,在双亲委派机制下,会让启动类加载器找到类库中的String类,而不会被我们自己写的String类覆盖。

如何打破双亲委派机制呢?

可以自定义类加载器

运行时数据区

类加载器把类的信息加载到内存时,存储到运行时数据区

根据运行时数据区的不同功能,可以分为五个部分:

程序计数器

程序计数器是记录线程执行的指令集的位置,因为线程在执行时,CPU要进行切换执行,所以要记录线程执行的位置。 java是高级语言,每一条高级语言要分解成好几条指令执行

特点:

1.程序计数器是占用内存最小的一块区域,也是运行速度最快的区域

2.程序计数器是线程私有的,每个线程都有属于自己的程序计数器,程序计数器的生命周期和线程的生命周期一样

3.程序计数器是运行时数据区唯一一个不会出现内存异常的区域

虚拟机栈

虚拟机栈是运行单位,管理程序如何执行,一个方法被调用,就入栈,运行结束,就出栈

虚拟机主要用来运行Java语言写的方法

特点:

1.线程私有的,每个线程中调用的方法都在线程对应的虚拟机栈中运行

2.A线程的方法无法调用B线程中的方法

3.栈中存储局部变量

4.虚拟机栈中不存在垃圾回收

5.虚拟机栈中存在内存溢出(例如 递归调用太深)

一个方法被压入到虚拟机栈中以后称为栈帧

栈帧的内部结构:

局部变量表(存储局部变量的值)

java 复制代码
    public void test() {
​
        int a=10;//基本类型存储他的值
        int b=10;
        String s=new String();//引用类型存储他的地址,地址在堆中
    }

操作数栈:就是用来计算的区域

java 复制代码
    public void test() {
​
        int a=10;//先从局部变量表中拿到a和b 的值
        int b=10;
        int c = a + b;//然后到操作数栈中进行计算,把运算结果赋给c,然后把写回到局部变量表中
    }

本地方法栈

本地方法:在Java程序中,不由Java语言编写的方法

本地方法是由native修饰,没有方法体

因为Java是上层语言(开发上层应用程序),没有权限直接与硬件进行交互(比如读取内存或硬盘中的数据)

本地方法栈:本地方法栈是运行单位,一个本地方法被调用,就入栈,运行结束,就出栈

特点:

1.线程私有

2.没有垃圾回收

3.存在内存溢出情况

堆:用来存储Java中创建的对象

特点:

1.线程共享的

2.可以通过设置参数来改变堆的大小

3.存在内存溢出情况

4.是GC(Garbage Collection)垃圾回收的重点区域

5.是运行时数据区内存最大的一块空间

堆内存区域划分

新生代(区):伊甸园区 幸存者0 幸存者1

老年代(区)

为什么要分代(区)

根据对象的存活时间,大小放在不同的区域,不同的区域有不同的垃圾回收算法

会频繁的回收新生代,较少回收老年代

可以对回收算法扬长避短

对象创建分配内存的过程

1.第一次创建的对象一般都分在伊甸园区(内存很大的对象会直接分配到老年区)

2.当垃圾回收来临时,伊甸园区没有被回收的对象进入幸存者0区,清空伊甸园区

3.当下一次垃圾回收来临时,伊甸园区和幸存者0区没有被回收的对象进入幸存者1区,伊甸园区和幸存者0区被清空

4.当一个对象在经历15词垃圾回收后,他就会进入老年区

堆空间参数:

涉及到jvm调优

理解是要根据实际的需要,来调整jvm中原有的一些参数,比如堆的初始化大小

官网地址:

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

-XX:+PrintFlagsInitial

查看所有参数的默认初始值

-Xms:初始堆空间内存

-Xmx:最大堆空间内存

-Xmn:设置新生代的大小

-XX:MaxTenuringTreshold:设置新生代垃圾的最大年龄

-XX:+PrintGCDetails 输出详细的 GC 处理日志

方法区

方法区主要是用来加载类信息的(变量,方法,静态常量...)以及编译器编译后的字节码

特点:

1.方法区也是线程共享的

2.也存在内存溢出情况

3.方法区的大小也是可以设置的,方法区的大小决定 可以加载多少类

方法区的垃圾回收

方法区也存在垃圾回收,主要是回收类信息

但是类信息回收是比较苛刻的:

1.该类以及派生的子类所创建的对象全都已经不再被使用,而且已经被回收了

2.该类的类加载器被回收了

3.该类的Class对象也不存在了,如果Class对象还存在,那么就说明可能该类信息还会被使用

本地方法接口

虚拟机中负责调用本地方法的入口,本地方法在本地方法栈中(运行时数据区)运行

什么是本地方法: 被nativa修饰,没有方法体的方法,是操作系统提供的方法

为什么要调用本地方法:因为Java是上层开发应用程序的语言,无法和计算机的硬件(内存,硬盘,外设(喇 叭))交互,需要用本地操作系统提供的方法

执行引擎(黑盒)

执行引擎在虚拟机中主要负责将虚拟机中的字节码 编译/翻译 成机器码

.java------>jdk(javac)编译------>.class 开发阶段

.class------>执行引擎编译------>机器码 运行阶段

什么是解释器?什么是JIT编译器?

解释器/解释执行: 比如sql,HTML,css,js,python,不需要整体编译,而是由解释器一行一行的执行

特点:执行速度慢,但不需要花费时间编译

编译执行:先把代码整体进行编译,生成另一种文件格式

特点:执行速度快,但需要花费时间编译

jvm的执行引擎在将字节码编译为机器码的时候,采取的是半解释,半编译的机制

开始时,可以先采用解释执行,立即投入到编译工作中

等到编译好之后,采用编译执行

垃圾回收(重点)

什么是垃圾?

垃圾就是内存中已经没有任何引用指向的对象就叫做垃圾对象

如果不对垃圾进行回收,会导致新的对象没有足够的空间存储,进而导致内存溢出(Out Of Memory 简称OOM)

早期的垃圾回收

早期的垃圾回收都是手动释放 malloc() free()

如果忘记回收垃圾。那么就会造成内存泄漏(对象不再使用,但还占据着内存空间)

垃圾回收的重点区域

堆: 频繁回收新生代,较少回收老年代

方法区:虽然回收条件比较苛刻,但是也存在垃圾回收

内存溢出和内存泄漏

内存溢出:内存空间不足以运行程序,会报出内存异常(Out Of Memory 简称OOM)---->ERROR 一般程序我

们只能处理异常

内存泄漏:当一些对象已经不再被使用,却没有被回收,会一直占据着内存

比如: 对象中提供了close();方法的对象

1.数据库链接对象

2.Socket链接对象

3.IO读取文件的对象

垃圾回收相关算法

垃圾标记阶段算法

垃圾标记阶段的目的:判断那些对象是垃圾对象

垃圾标记阶段涉及两个算法:引用计数算法(现在的虚拟机中已经不再使用了)

可达性分析算法

引用计数算法

思想:给对象加入一个字段用来记录该对象被引用的数量,有一个引用指向对象,计数器就加一,有一个引用断

开,计数器就减一。

优点:实现思路简单

缺点: 1.会造成额外的时间和空间开销

2.不能解决对象循环引用的问题,比如ABC三个对象循环引用,但是可能与外界没有引用,此时虽然三个对象的计数器都是1 ,但是他们并没有被引用

可达性分析算法(根搜索算法,也可以叫追踪性分析算法)

思想:从一些活跃的对象开始进行搜索,只要和和根对象有关联的对象,就不是垃圾对象,如果和根对象没有关

联,即使和其他对象存在引用关系,也会被判定为垃圾对象。

可以成为根对象的对象:

1.虚拟机栈中对象,运行中的方法引用的对象

2.虚拟机内部的Class对象,类加载器,异常类

3.类中的一些静态成员变量

4.与同步锁有关的 对象

对象的finallization机制

主要是Object类中的finalize()方法------------------------------------>final finally finalize区分

finalize方法在对象被判定为垃圾对象之后,在被垃圾回收真正回收之前由虚拟机执行

在finalize中执行最后要执行的功能

finalize只被执行一次

由于finalize()存在,所以对象分为三种状态:

1.可触及的:有引用指向的对象

2.可复活的:已经被判定为垃圾对象,但是还没有被回收,可能在finalize()方法中复活

3.不可触及的:finalize已经被执行过,又被判定为垃圾对象,最后被回收

垃圾回收阶段算法

标记-复制算法

把内存分为几块,把正在使用的内存块的存活对象,复制到另一块内存中,从内存块的位置开始摆放,清空正在使用的内存。

对象会被移动,适合存活对象少,垃圾对象多的情况,特别适合回收新生代的垃圾对象。

标记-清除算法:

存活对象不会被移动,把垃圾对象的地址记录在一个空闲列表中,有新的对象到来时,把新的对象分配到空闲列表中的内存地址上,覆盖垃圾对象。

适合老年代的回收,因为存活对象多且内存占用大,不需要移动对象

标记-压缩算法

将存活对象压到内存的一端,重新排列,将边界外的空间清理,释放内存碎片,效果就像进行了一次标记-清除算法之后,进行了一次内存碎片整理。

也是适合老年代的垃圾回收

分代收集

为什么要用分代收集?

垃圾回收器

标记阶段和回收阶段的算法都是理论层面的,而垃圾回收器而垃圾回收的实践者

垃圾回收器分类:

**按照线程数量分:**单线程垃圾回收器------------>只有一个线程进行垃圾回收

多线程垃圾回收器------------>多个线程进行垃圾回收

**从工作模式分:**独占式垃圾回收器,当垃圾回收线程执行时,其他的线程会暂时停止执行

(STW ------>stop the world)

并发式垃圾收集器,当垃圾线程执行时,可以允许其他线程并发执行

从回收的空间分:新生代垃圾回收器

老年代垃圾回收器
HotSpot 垃圾收集器
图中展示了 7 种作用于不同分代的收集器,如果两个收集器之间存在连线, 则说明它们可以搭
配使用。虚拟机所处的区域则表示它是属于新生代还是老年代 收集器。
Serial,Serial Old,ParNew,Parallel Scavenge,Parallel Old,CMS,G1

两款很重要的垃圾回收器

CMS(Concurrent Mark Sweep, 并发标记清除)

这款垃圾回收器首创了垃圾回收线程和其他线程同时执行
CMS收集器是以获取最短回收停顿 时间为目标的收集器(追求低停顿),它在垃圾收集时使得
用户线程和 GC 线程并发执行,因此在垃圾收集过程中用户也不会感到明显的卡顿。

初始标记:垃圾回收线程独占的

并发标记:用户程序线程和垃圾回收线程同时执行

重新标记:垃圾回收线程独占的

并发清理:其他线程和垃圾回收线程同时执行

重置线程:用户程序线程和垃圾回收线程同时执行

G1(Garbage First)回收器

G1是面向服务器端的垃圾回收器,也继承了CMS中用户线程和垃圾回收线程同时执行的特点

G1 垃圾回收期可以做到对整个堆进行垃圾回收,不再区分新生代,老年代

垃圾回收性能高

为什么叫G1(垃圾优先)

把每个分区又分为若干个小的区域,会对每个小的区域进行跟踪,优先回收垃圾最多的区域


查看 JVM 垃圾回收器设置垃圾回收器

打印默认垃圾回收器

-XX:+PrintCommandLineFlags -version

JDK 8 默认的垃圾回收器

年轻代使用 Parallel Scavenge GC

老年代使用 Parallel Old GC

打印垃圾回收详细信息

-XX:+PrintGCDetails -version

设置默认垃圾回收器

Serial 回收器

-XX:+UseSerialGC 年轻代使用 Serial GC, 老年代使用 Serial Old GC

ParNew 回收器

-XX:+UseParNewGC 年轻代使用 ParNew GC,不影响老年代。

CMS 回收器

-XX:+UseConcMarkSweepGC 老年代使用 CMS GC。

G1 回收器

-XX:+UseG1GC 手动指定使用 G1 收集器执行内存回收任务。

-XX:G1HeapRegionSize 设置每个 Region 的大小。

相关推荐
LGL6030A2 小时前
Java学习历程24——仿真智能集群项目(2)
学习
后来后来啊2 小时前
2026.1.22学习笔记
笔记·学习
Remember_9932 小时前
文件系统与IO操作:深入解析与Java实践
java·开发语言·数据结构·ide·python·算法
纪伊路上盛名在2 小时前
Chap2 Neural Networks with PyTorch
人工智能·pytorch·python·深度学习·机器学习
开开心心就好2 小时前
卸载工具清理残留,检测垃圾颜色标识状态
linux·运维·服务器·python·安全·tornado·1024程序员节
Nan_Shu_6142 小时前
学习: 尚硅谷Java项目之尚庭公寓(4)
学习
小舞O_o2 小时前
gitlab文件上传
linux·服务器·git·python·目标检测·机器学习·gitlab
yubo05092 小时前
Python 包、模块、导入规则
开发语言·python
进阶小白猿2 小时前
Java技术八股学习Day24
java·开发语言·学习