JVM基础篇

news2024/11/16 13:27:10

什么是JVM

java虚拟机

JVM的功能

1.解释和运行

对字节码文件中的指令,实时的解释成机器码,让计算机执行

2.内存管理

自动为对象、方法等分配内存

自动的垃圾回收机制,回收不再使用的对象(c++不会自动回收,相当于降低了编程的下限)

3.即时编译(JIT)

对热点代码进行优化,提升执行效率

java需要实时解释是为了不同平台的兼容性,然而由于需要实时编译,所以性能方面比不上c++,所以就需要即时编译

即时编译的过程

把热点代码(就是频繁出现的代码)放到内存,这样下次就不用再编译了,提高性能。

java虚拟机的组成

1.类加载器(加载字节码文件到内存)

2.运行时的数据区域(JVM管理的内存) 负责管理jvm使用到的内存,比如对象的创建和销毁

3.执行引擎(即时编译器、解释器、垃圾回收器等)(将字节码内的指令解释成机器码,同时视同JIT即时编译器优化性能)

4.本地接口(调用本地编译的方法,比如c/c++实现的方法 native方法)

字节码文件的组成

jclasslib查看字节码文件

字节码文件的组成

魔数:java字节码文件,将文件头称为魔数

主副版本号:jdk1.2是46 之后每升级一个大版本就+1 比如jdk1.8就是46+6 52

主版本号作用就是判断当前字节码的版本和运行时的jdk是否兼容

常量池:避免相同的内容重复定义,节省空间

类的生命周期

 五个阶段:加载、链接、初始化、使用、卸载

加载阶段:第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息,第二步类加载器加载完类以后,java虚拟机就会把字节码中的信息放到一个内存区里面(就是方法区),第三步生成一个INstanceklass对象,保存类的所有信息,里面还包含实现特定功能的信息如多态信息。

第四步:同时,java虚拟机还会在堆中生成一份与方法区中数据类似的java.lang.class对象。

作用是在java代码中去获得类的信息一级存储静态字段的数据。

问:既然都方法区都有信息了,为什么还要在堆区存储信息?

答:方法区中的instanceklass是用c++实现的,对于我们java程序是不好直接操作的。所以我们要转换为java.lang.class对象才好操作。并且堆区的字段少于instanceklass,不是所有字段都需要访问的,所以要控制开发者的访问范围

连接阶段:有三个小阶段 验证、准备。解析。

验证:检验程序的内容是否符合java虚拟机的规范

准备:准备阶段为静态变量(static)分配内存并设置初始值(这里的初始值是指默认值 不是你赋的值 比如int的初始值就是0)。

但是如果是final修饰的静态变量的话,在准备阶段就会赋你给定的值,不用等到初始化阶段。

解析阶段:将常量池的符号引用替换成直接引用。

符号引用:在字节码文件中使用编号来访问常量池的内容。 

直接引用:不再使用编号,直接使用内存的地址进行访问具体数据

初始化阶段:会执行静态代码块中的代码,并为静态变量赋值。

从字节码角度分析就是执行字节码文件中clinit部分的字节码指令

  

初始化阶段不一定存在

出现继承的初始化阶段:

  

 final修饰的变量如果赋值的内容需要执行指令才能出结果,那么就会执行clinit指令进行初始化

类加载器

java虚拟机提供给应用程序去实现获得类和接口字节码数据的技术

类加载器分为两类 一类是java代码实现 一类是java虚拟机底层源码实现的

类加载器在jdk8和8之后的差别很大

 

  启动类加载器:用于加载java安装目录/jre/lib下的类文件

可以帮我们扩展我们要用的核心类

1.放入jre/lib进行扩展(不推荐使用)

2.使用参数进行扩展

 扩展类加载器:用于加载/jre/lib/ext下的类文件

类似于启动类加载器 我们也可以通过扩展类加载器去加载用户的jar包

  应用程序类加载器:用于加载classpath下的类文件 

类加载器的双亲委派机制(重点)

这东西的核心就是解决一个类到底由谁加载的问题(有多个类加载器)

作用:

1.保证类加载的安全性

2.避免重复加载

双亲委派机制就是:当一个类加载器接收到加载累的任务时,会自底向上查找是否加载过,再自顶向下进行加载。 

 现在思考一个问题

如果一个类 三个加载器都能加载 那应该谁加载?启动类加载器

面试题

 打破双亲委派机制

三种方式:自定义类加载器 线程上下文类加载器 Osgi框架的类加载器

 为什么要打破?

  自定义类加载器打破双亲委派机制

双亲委派机制的核心代码是在loadclass()方法里面,只要重写这个方法,把核心代码删了,就可以打破双亲委派机制了。

自定义类加载器默认的父类加载器是应用程序类加载器

问题

 但是考虑到loadclass方法是通过调用findclass实现的双亲委派机制,所以真正实现一个自定义类加载器的方式是重写findclass方法。这样就不会破坏双亲委派机制,并且创建一个新的自定义类加载器。

线程上下文类加载器

SPI机制是JDK内置的一种服务提供发现机制

思考一个问题,spi机制是如何拿到应用进程的应用程序类加载器的?(因为DriveManger是由启动类加载器加载的,但是SPI最后却可以用应用进程类加载器加载DriveManger)

OSGI框架打破双亲委派机制

热部署

 注意事项

JDK9之后的类加载器

JDK9引入了module概念 类加载器的设计发生了变化 (jdk的类不再位于jar包中,而是放到一个jmod文件夹里面)

启动类加载器不再使用c++编写,直接使用java编写,位于jdk.internal.loader.ClassLoaders

拓展类加载器变成平台类加载器

 由于JDK9之后是用模块化的设计思路,所以其实平台类加载器是没什么用的,它的存在只是为了和老版本兼容

 

JVM的第二部分运行时数据区域(JVM管理的内存)

 运行时数据区分为 程序计数器、java虚拟机栈、本地方法栈(这些都是线程不共享的)。

方法区、堆(线程共享的)

程序计数器也叫pc寄存器,每个线程都会通过程序计数器记录当前要执行的字节码指令的地址

作用:

1.控制程序指令的进行,实现分支、跳转、异常等逻辑

2.在·多线程的情况下,可以保存当前指令的内存地址,以确保下一次的执行

内存溢出:程序在使用某一块内存区域的时候,存放的数据需要占用的内存大小超过了虚拟机能提供的内存上限。

思考一下 程序计数器会不会出现内存溢出?

当然不会 每个线程只存储一个固定长度的内存地址,程序计数器是不会发生内存溢出的

程序员不需要对程序计数器做任何处理,java虚拟机实现的

 本地方法栈是由c++实现的方法  java虚拟机栈是由java实现的

java虚拟机栈随着线程的创建而创建,而回收则在线程销毁时进行。由于方法可能会在不同线程中执行,每个线程都会包含一个自己的虚拟机栈

 栈帧的组成:局部变量表 操作数栈 帧数据 

局部变量的作用是在方法执行过程中存在所有的局部变量。编译成字节码文件时,就可以确定局部变量表的内容

 

 操作数栈

存放临时数据,一般都是把临时数据存到操作数栈中,当要用到这个数据了 就会取出来 存到局部变量表中。在编译期就可以确定操作数栈的最大深度。

 帧数据

动态链接

 当我们在使用别的类的方法和属性时,在链接阶段是不会把fai该符号引用变成直接引用的,所以我们要用动态链接 吧这个符号引用的内存地址保存在运行的常量池里面

方法出口

方法出口是指方法在正确或异常结束时,当前栈帧会被弹出,同时程序计数器应该指向上一个栈帧中的下一个指令的地址。所以在当前栈帧中,需要存储此方法的出口地址(简单来说就是在一个方法被弹出栈之前,他会告诉你下一个方法的栈帧执行到哪里了)

异常表

异常表存放代码中异常的处理信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置(简单来说就是存放trycatch等处理异常的执行流程)

 那么问题来了 Java虚拟机栈会出现内存溢出吗?

答案是有可能的

死递归就是栈内存溢出吧

java虚拟机栈的默认大小是根据操作系统和计算机体系结构决定的

一般都不会栈内存溢出 一般都是程序员操作失误 写了一个死递归才会这样

设置虚拟机栈大小

一般情况下,工作中使用了递归操作,栈的深度也就几百,不会出现栈溢出的情况,所以可以设置成-Xss256k节省内存。

 本地方法栈

 创建出来的对象都在堆上(空间占用最大的一块区域)

在栈内存里面会保存这个对象在堆内存里面的地址这样就可以调用了。

 堆内存会不会溢出?

会的

 堆内存有三个要关注的值:used total max

其实不是滴,堆内存的溢出判断条件比较复杂,会在《垃圾回收器》中详细讲

手动设置max和total

在开发中,直接把max和total设置成相同的数值,这样不用重复申请,减少开销

方法区

方法区存放基础信息的位置、线程共享

主要包括三部分:类的元信息、运行时常量池、字符串常量池

这里的常量池(静态常量池)一般都是放引用 不是放数据 真正的数据在运行时常量池

运行时常量池

方法区在每个虚拟机上的实现不是都一样的

方法区的溢出 :不停往方法区添加类的信息就可以了

字符串常量池

放的就是字符串常量

 字符串常量池和运行时常量池有什么关系?

在早期设计中,字符串常量池是运行时常量池的一部分,他们存储的位置都是一样的,后面做了拆分

但如果是这样

返回的结果就是true

区别就在于

String的intern()方法可以手动将字符串放到字符串常量池中

jdk6的版本

jdk7及以后的版本

这里要注意的是字符串常量池里面的java是在虚拟机加载的时候就已经被加载进来了,因为虚拟机要用到。所以java资格字符串不是后面加入的

静态变量到底存放在哪里?

jdk6和之前的版本是放在方法区,也就是永久代中。

jdk7以后的版本就放在堆里面的class对象中,脱离了永久代

 直接内存

直接内存不在java虚拟机的规范中,所以并不属于java运行时的内存区域

主要是为了解决一个特定的问题

想要在直接内存中创建数据 可以使用ByteBuffer

 直接内存也是可以设置大小的

思考 运行时数据区分为哪几部分,每一部分的作用是什么?

 不同JDK版本之间运行时数据区域的区别是什么?

jdk6

jdk7

 

jdk8

 自动垃圾回收

在c/c++没有自动垃圾回收,如果一个对象不再使用,需要手动释放,否则就会出现内存泄漏。我们称释放对象的过程为垃圾回收。

内存泄漏指的是不再使用的对象在系统中未被回收,内存泄漏的积累可能会导致内存溢出。

java的内存管理

java为了简化对象的释放,引入了垃圾回收(GC)机制。通过垃圾回收期对不再使用的对象完成自动的垃圾回收,垃圾回收期主要负责对上的内存进行回收。

自动垃圾回收:方法区和堆(主要是堆)

 

思考一下 运行时的数据区有五部分:程序计数器 java虚拟机栈 本地方法栈 方法区 堆 那么为什么GC只对方法区和堆进行回收?

因为程序计数器 java虚拟机栈 本地方法栈是线程不共享的,它们随着线程的创建而创建,随着线程的销毁而销毁。而方法的栈帧在执行完方法之后就会自动弹出栈并释放掉对应的内存

方法区的回收:

主要回收不再使用的类

一个类可以被卸载有三个条件

堆回收

什么对象可以被回收:如果对象被引用了就不能回收

再思考一下,如果上图的a1=null b1=null 那么可不可以回收A和B对象呢?

可以的 方法中已经没有办法使用引用去访问A和B对象了(即使他们的属性有相互引用)

常用的两种判断方法:引用计数法和可达性分析法

 引用计数法会为每一个对象维护一个引用计数器,当对象被引用的时候加1,取消引用的时候减1

 那么这个的缺点就很明显了,那么java是不是解决了这个循环引用的问题? 其实不是 java是用了另外一个方法:可达性分析算法

可达性分析算法

可达性分析算法将对象分为两类:垃圾回收的根对象(GCRoot)和 普通对象,对象与对象之间存在引用关系

 

首先确定的是这个GC Root对象是不可以被回收的,java会给一个GCRoot表去保存这些GcRoot

 什么对象可以被称为GCRoot对象

  

这里关联栈内存的线程对象是主线程对象

可达性软法的引用的强引用,除了强引用以外,java中还有很多引用

软引用

弱引用

虚引用

终结器引用

软引用

软引用是比较弱的引用关系,如果一个对象只有软引用关联它,当程序内存不足的时候,就会将软引用中的数据进行回收

由于软引用可以被回收,所以软引用一般不用于引用重要的数据,所以一般用于缓存。

如果软引用的引用对象被回收了,那么我们是不是应该把软引用对象也回收掉?

答案是肯定要回收的,,那么怎么回收?

 弱引用

与软引用基本一致,区别就是弱引用包含的对象在垃圾回收机制时,不管内存够不够都会直接被回收。

虚引用和终结器引用

不会在开发使用 

 垃圾回收算法

核心思想:

1.找到内存中存活的对象

2.释放不再存活对象的内存,使得程序能再次利用这部分空间

有四种垃圾回收算法:标记-清除算法 复制算法 标记-整理算法 分代GC

 

评价标准:1.吞吐量 2.最大暂停时间 3.堆使用效率

 

标记回收算法

分为两个阶段:

标记阶段:将所有存活的对象进行标记。java中用可达性分析算法,从GCRoot开始通过引用链遍历出所有存活对象。

清除阶段:从内存中删除没有被标记也就是非存活对象

优缺点

复制算法

把整个堆内存的空间分为From和To空间,每次在对象分配阶段,只能使用其中一块空间(From空间)。

 优缺点

标记整理算法

也叫标记压缩算法,是对吊机清理算法中容易产生内存碎片问题的一种解决方案。

 分代GC算法-----分代垃圾回收算法

分代GC会把整个内存区域分为年轻代(存活时间短的对象)和老年代(存活时间长)

年轻代中还有几个小区

调整内存的大小

回收的步骤

为什么分代GC算法把堆分成年轻代和老年代

 1.可以通过调整年轻代和老年代的比例适应不同类型的应用程序,提高内存的利用率和性能。

2.新生代和老年代使用不同的垃圾回收算法,新生代一般选择复制算法,老年代可以选择标记-清除和标记-整理算法,由程序员来选择灵活度较高。

3.分代的设计中允许只回收新生代,如果能满足对象分配的要求就不需要对整个堆进行回收(full gc),STW时间就会减少。

垃圾回收器

年轻代-serial垃圾回收器

serial是一种单线程串行回收年轻代的垃圾回收器

老年代-SerialOld垃圾回收器

SerialOld是Serial的老年代版本,也是采用单线程串行回收 用的是标记-整理算法

年轻代-ParNew垃圾回收器

ParNew就是对Serial在多线程CPU下的优化,使用多线程进行垃圾回收

老年代-CMS(Concurrent Mark Sweep)垃圾回收器

CMS垃圾回收器关注的是系统的暂停时间,允许用户线程和垃圾回收线程在某些步骤中同时执行,减少了用户线程等待的时间。

 

过程

 那么有什么缺点呢?

 年轻代-Parallel Scavenge垃圾回收器

ps是JDK8默认的年轻代垃圾回收器,多线程并行回收,关注的是系统的吞吐量。具备自动调整堆内存大小的特点。

老年代-Parallel Old垃圾回收器

 

G1垃圾回收器

G1垃圾回收有两种方式:

1.年轻代回收(Young Gc)

2.混合回收(Mixed GC) 年轻代和老年代都要回收

 

 G1在进行YoungGC 的过程中会去记录每次垃圾回收时每个Eden区和Survivor区的平均耗时,以作为下次回收时的参考依据。这样就可以根据配置的最大暂停时间计算出本次回收时最多回收多少个Region区域了。

 

 混合回收

 这里和cms长的差不多,但是有点区别 cms的最终标记阶段会把上一个用户线程新创建的对象也标记,但是G1这里不会 并且最后的用户清理 G1用的是复制算法 不会产生内存碎片。但是cms用的是标记清除算法

FULL GC

如果FULLGC都进行不了就会产生内存溢出

 1.方法区一般不需要回收,jsp等技术会通过回收类加载器去回收方法区中的类

堆就由垃圾回收器回收

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1517372.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT 如何防止 QTextEdit 自动滚动到最下方

在往QTextEdit里面append字符串时,如果超出其高度,默认会自动滚动到QTextEdit最下方。但是有些场景可能想从文本最开始的地方展示,那么就需要禁止自动滚动。 我们可以在append之后,添加如下代码: //设置编辑框的光标位…

指针的函数传参的详细讲解(超详细)

如果对指针基础知识已经有可以直接跳到 函数的指针传参与解引用,哪里不明白可以评论,随时解答。 目录 所以就有了一句话:指针就是地址,地址就是指针 对于指针在C语言中,指针类型就是数据类型,是给编译器…

PHP极简网盘系统源码 轻量级文件管理与共享系统网站源码

PHP极简网盘系统源码 轻量级文件管理与共享系统网站源码 极简网盘是一个轻量级文件管理与共享系统,支持多用户,可充当网盘程序,程序无需数据库 安装步骤: 1.建议安装在apache环境下,并确保.htaccess可用 2.解压文件…

论文阅读——RingMo

RingMo: A Remote Sensing Foundation Model With Masked Image Modeling 与自然场景相比,RS图像存在以下困难。 1)分辨率和方位范围大:受遥感传感器的影响,图像具有多种空间分辨率。此外,与自然图像的实例通常由于重…

【Python】Leetcode 240. 搜索二维矩阵 II - 削减矩阵+递归,击败88%

描述 搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。 该矩阵具有以下特性: 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。 思路 确定左右及上下限,削减矩阵,递归。 注意判断四个端…

15届蓝桥杯第一期模拟赛所有题目解析

文章目录 🧡🧡t1_字母数🧡🧡问题描述思路代码 🧡🧡t2_大乘积🧡🧡问题描述思路代码 🧡🧡t3_星期几🧡🧡问题描述思路代码 🧡…

Spring基础——使用注解开发SpringMVC

目录 配置SpringMVC的初始化信息配置ServletWebApplicationContext配置RootWebApplicationContext配置ServletContext 创建Controller控制器配置Controller响应路径接收用户传递参数接收JSON数据接收简单类型对象封装参数 接收数组类型 Restful 文章源码仓库:Spring…

大模型笔记:吴恩达 ChatGPT Prompt Engineering for Developers(1) prompt的基本原则和策略

1 intro 基础大模型 VS 用指令tune 过的大模型 基础大模型 只会对prompt的文本进行续写 所以当你向模型发问的时候,它往往会像复读机一样续写几个问题这是因为在它见过的语料库文本(通常大多来自互联网)中,通常会连续列举出N个问…

Flask vs. Django:选择适合你的Web开发框架【第134篇—Flask vs. Django】

Flask vs. Django:选择适合你的Web开发框架 在选择一个适合你项目的Web开发框架时,常常会遇到 Flask 和 Django 这两个流行的选择。两者都有其优势和适用场景,本文将探讨它们的特点,并通过代码实例和解析来帮助你更好地做出选择。…

环形链表的起点——细节讲解

对于一个环形链表,我们要找到他的起点。可以通过如下推导。 我们设置两个快慢指针,相遇的点为X. 到起点的距离是T,圈长是C,第一次相交的点是X (TX)2TNCX 化出来TN*C-X 也就是说我们把一个节点放头部重新遍…

SQLiteC/C++接口详细介绍之sqlite3类(六)

快速前往文章列表:SQLite—系列文章目录 上一篇:SQLiteC/C接口详细介绍之sqlite3类(五) 下一篇:SQLiteC/C接口详细介绍之sqlite3类(七) 19. sqlite3_changes与sqlite3_changes64 是SQLite中用…

手机安装Kali Linux

在数字化时代,信息安全和隐私保护显得尤为重要。Kali Linux,作为一款专业的渗透测试和安全审计工具,因其强大的功能和丰富的资源库而受到广大安全研究者和爱好者的青睐。然而,我们通常只能在传统的电脑设备上安装和使用Kali Linux…

外卖小程序-购物车模块表结构设计和后端代码

表结构设计 添加购物车代码 Service public class ShoppingCartServiceImpl implements ShoppingCartService {Autowiredprivate ShoppingCartMapper shoppingCartMapper;Autowiredprivate DishMapper dishMapper;Autowiredprivate SetmealMapper setmealMapper;/*** 添加购物…

在Linux/Ubuntu/Debian中使用7z压缩和解压文件

要在 Ubuntu 上使用 7-Zip 创建 7z 存档文件,你可以使用“7z”命令行工具。 操作方法如下: 安装 p7zip: 如果你尚未在 Ubuntu 系统上安装 p7zip(7-Zip 的命令行版本),你可以使用以下命令安装它:…

某夕夕商品数据抓取逆向之webpack扣取

逆向网址 aHR0cHM6Ly93d3cucGluZHVvZHVvLmNvbQ 逆向链接 aHR0cHM6Ly93d3cucGluZHVvZHVvLmNvbS9ob21lL2JveXNoaXJ0 逆向接口 aHR0cHM6Ly9hcGl2Mi5waW5kdW9kdW8uY29tL2FwaS9naW5kZXgvdGYvcXVlcnlfdGZfZ29vZHNfaW5mbw 逆向过程 请求方式:GET 参数构成 【anti_content】…

PHP中的反序列化漏洞

PHP中的反序列化漏洞 目录 PHP 中的序列化与反序列化 概述 序列化 基本类型的序列化 对象的序列化 反序列化 示例序列化与反序列化 反序列化漏洞 - PHP 中的魔术方法 - Typecho_v1.0 中的反序列化漏洞 POP链的构造思路 pop链案例 反序列化逃逸 字符串逃逸&#xff…

GoLang:云原生时代致力于构建高性能服务器的后端语言

Go语言的介绍 概念 Golang(也被称为Go)是一种编程语言,由Google于2007年开始设计和开发,并于2009年首次公开发布。Golang是一种静态类型、编译型的语言,旨在提供高效和可靠的软件开发体验。它具有简洁的语法、高效的编…

C# wpf 使用GDI实现截屏

wpf截屏系列 第一章 使用GDI实现截屏(本章) 第二章 使用GDI实现截屏 第三章 使用DockPanel制作截屏框 第四章 实现截屏框热键截屏 第五章 实现截屏框实时截屏 第六章 使用ffmpeg命令行实现录屏 文章目录 wpf截屏系列前言一、导入gdi32方法一、NuGet获取…

【LeetCode: 102. 二叉树的层序遍历 + bfs】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

国产化三防笔记本丨亿道国产加固笔记本FT-2000/4处理器

国产化加固笔记本是指采用国产操作系统和处理器,通过技术手段对其进行硬件加固、软件加密、数据安全等多方面加强处理的产品。这种笔记本电脑通常被用于政府项目、金融行业等对安全性要求极高的领域。 在国产化加固笔记本中,硬件加固是重要的一环。为了保…