进阶--jvm

server/2025/3/3 20:31:30/

目录

jvm%E9%83%A8%E5%88%86-toc" name="tableOfContents" style="margin-left:40px">jvm部分

jvm%E7%9A%84%E4%BD%9C%E7%94%A8-toc" name="tableOfContents" style="margin-left:120px">jvm的作用

jvm%E5%86%85%E9%83%A8%E6%9E%84%E9%80%A0-toc" name="tableOfContents" style="margin-left:120px">jvm内部构造

垃圾回收部分

类加载系统

类加载过程

类在哪些情况下被加载

类在以下两种情况下,是不会被加载的

运行时数据区

程序计数器

本地方法栈

堆空间区域划分

为什么分区(代)

对象创建存储过程:

JVM调优

方法区

方法区的垃圾回收

本地方法

什么是本地方法?

本地方法接口

java中为什么要调用本地方法?

执行引擎

java程序执行过程中涉及两次编译

将字节码转为机器码有两种方式:

为什么要使用解释执行和编译执行并存这样的设计?

垃圾回收

什么是垃圾对象?

垃圾回收发展

哪些区域会出现垃圾回收

内存溢出与内存泄漏

stop the world

垃圾回收阶段算法

1.垃圾标记阶段

2.垃圾回收阶段

垃圾回收器

什么是垃圾回收器

垃圾回收器分类:


jvm%E9%83%A8%E5%88%86" name="jvm%E9%83%A8%E5%88%86">jvm部分

jvm%E7%9A%84%E4%BD%9C%E7%94%A8" name="jvm%E7%9A%84%E4%BD%9C%E7%94%A8" style="background-color:transparent">jvm的作用

jvm负责把编译后的字节码转换为机器码

jvm%E5%86%85%E9%83%A8%E6%9E%84%E9%80%A0" name="jvm%E5%86%85%E9%83%A8%E6%9E%84%E9%80%A0">jvm内部构造

类加载部分: 负责把硬盘上字节码加载到内存中(运行时数据区)

运行时数据区: 负责存储运行时产生的各种数据 类信息 ,对象信息,方法信息...

执行引擎: 负责将字节码转为机器码

本地方法接口: 调用本地方法,启动线程 start0( );Object类中的 hashcode( )--对象内存地址

public native int hashcode();

private native int read0() throws IOException;

垃圾回收部分

类加载系统

类加载系统负责将硬盘上的字节码文件加载到jvm中,生成类的class对象,存储在方法区.

类就是一个模版.

类加载过程

1.加载

以二进制文件流进行读取

在内存中为类生成class对象

2.链接

验证: 验证字节码的结构是否正确

准备: 为类的静态属性进行初始化赋值

解析: 把字节码的符号引用 替换成 内存中的直接引用地址

3.初始化

初始化阶段主要是为类中静态成员进行赋值

因为类加载执行完初始化阶段,才说明了类被加载完成了

类在哪些情况下被加载

调用类中静态成员(变量,方法)

new 类的对象

在类中执行main( )

反射加载类 Class Class.forName("地址")

子类被加载

类在以下两种情况下,是不会被加载的

1.类作为数组类型

Demo demo[]=new Demo[10];//new的数组对象,Demo类型的,不是Demo对象

2.只是访问类中的静态常量

System.out.println(Demo.P);//优化    不加载整个类了,只获取到静态常来那个方法

类加载器

类加载器就是实际负责读取类的功能

类加载器分类:

站在jvm的角度上,分为

引导类加载器(不是用java写的,是c/c++),负责读取加载Java中底层系统库

java写的类加载器(用来读取我们写的应用程序)

运行时数据区

程序计数器

程序计数器用来记录每一个线程执行的指令位置

本地方法栈

本地方法栈只用来执行调用的本地方法的.

是线程私有的,不会存在垃圾回收,会出现内存溢出问题

堆的作用是用来存储java语言产生的对象的.

是运行时数据区中最大的一块内存空间,空间大小可以设置,

对空间是所有线程共享的.

对空间是垃圾回收的重点区域,堆中没有被使用到的垃圾对象,会被垃圾回收器收调

堆空间区域划分

堆分为

新生区(新生代 年轻代)

伊甸园区

幸存者0区

幸存者1区

老年区(老年代)

为什么分区(代)

可以将不同生命周期的对象,存储在不同的区域,针对不同的区域采用不同的垃圾回收算法,使得垃圾回收策略更加优化.

对象创建存储过程:

新创建的对象都存储在伊甸园区,

当垃圾回收时,将还被使用的对象,转移至某一个幸存者区,将伊甸园区垃圾对象进行清除

当下次垃圾回收时,将伊甸园区存储获得对象与当前正在使用的幸存者区存活的对象 转移到另一个幸存者区(每次空闲一个幸存者区)

当一个对象经历过15次垃圾回收后,仍然存活,俺么就把该对象移动到老年代

老年代比较少的进行垃圾回收,在老年代空间不足时,对老年代会进行垃圾回收,

当回收后,内存仍然不足时,会除FULL GC (整堆收集 应尽量避免)

当整堆收集后仍然不够使用,那么就会出现内存溢出错误 ---OOM OutOfMemoryError

JVM调优

可以根据程序具体的使用场景,对运行时数据区的各种控件大小进行调整 例如堆,方法区对垃圾回收器进行选择

方法区

方法区主要用来存储加载的类信息

方法区的大小也是可以设置的

方法区也会进行垃圾回收,方法区也可能会出现内存溢出问题

方法区的垃圾回收

方法区的垃圾回收,是对类信息进行回收的

类信息如果不再被使用,类信息也可以被卸载

卸载条件:

该类所产生的对象都不存在了

该类的class对象,也不被使用了

加载该类的加载器也被回收了

本地方法

什么是本地方法?

在java中,被native关键字修饰的方法,没有方法体.不是用java语言实现的方法,是用C/C++在操作系统底层实现的方法.

(只有抽象方法(abstract/接口)没有方法体吗?不是,还有native关键字修饰的方法.)

例如:

Object 类中的hashcode( ); 获取对象内存地址 涉及到读取内存

IO中读文件(输入文件 操作硬盘) read0( );

启动线程(把线程注册到操作系统,才能进行调度) native void start0( );

本地方法接口

虚拟机中专门用来调用本地方法的接口

java中为什么要调用本地方法?

因为java属于应用层语言,有时候需要对硬件系统资源进行调用,

此时不方便,在一个系统资源不允许应用层程序直接调用

那么就需要通过本地方法来调用操作硬件资源.

解释:网页(css/javascript/pathon)

编译:java/c

执行引擎

执行引擎是虚拟机核心部件之一,

主要作用是将加载到虚拟机中的字节码 再次转换为机器码(字节码并不是系统能够直接执行的机器码)//将字节码转换为机器码

执行引擎可以通过解释/编译两种方式 实现将字节码转换为机器码

java程序执行过程中涉及两次编译

第一次 .java(源代码 通过jdk) javac:通过这个调编译器 -->.class文件 称为前端编译

第二次 通过执行引擎 将字节码编译成机器码 称为后端编译

将字节码转为机器码有两种方式:

解释器(解释执行):对字节码逐行进行解释翻译,重复性代码,也是每次都要解释执行,效率低.

编译器(编译执行):对某段字节码进行整体编译,然后存储起来,以后使用时不再需要编译了,效率高.编译器会针对执行过程中的热点代码进行编译,并缓存起来.

为什么要使用解释执行和编译执行并存这样的设计?

程序开始运行时,解释器可以立即发挥作用,投入使用

而编译器虽然执行效果高,但是前期需要对热点代码进行跟踪和编译,需要消耗时间.

垃圾回收

堆和方法区

什么是垃圾对象?

就是一个对象,不再被任何的引用指向.

每有任何引用指向的对象.

垃圾对象如果不清理,新的对象可能没有足够空间,可能会导致内存溢出问题.

垃圾回收发展

早期c/c++这类语言,内存管理都是手动的,使用时申请,使用完后手动释放,

有点:对内存管理更加精确,效率高

缺点:增加程序员负担,控制不好,容易出事(忘了释放,误操作内存空间)

后来发展为自动回收:

java,C#...都采用自动垃圾回收

优点:解放了程序员

缺点:会占用一些内存空间(垃圾不是出现后立即回收的),降低程序员管理内存能力.

哪些区域会出现垃圾回收

堆 对象 频繁回收年轻代 较少回收老年代

方法区 类信息卸载 整理收集时,会进行回收 FULL GC

内存溢出与内存泄漏

内存溢出:内存不够用了

内存泄漏:系统中那些用不到的,但是又不能回收的对象.

案例:单例对象

数据库连接对象,IO流,socket这些提供close( );的类.

用完之后,如果没有关闭,垃圾回收器,是不能主动回收这些对象的.

内存泄漏,虽然不能直接触发内存溢出,但是长期有对象不能被回收,也是导致内存溢出的原因之一.

stop the world

垃圾回收时,会经历两个阶段:一是标记阶段,二是回收阶段,

在标记和回收时,需要我们的用户线程暂停,不暂停标记和回收时可能会出现错标和漏标

垃圾回收阶段算法
1.垃圾标记阶段

将虚拟机中不再被任何引用指向的队象标记出来,在垃圾回收阶段,就会将标记出来对象进行回收.

垃圾标记阶段相关算法

引用计数算法(存在缺陷,没有被虚拟机所使用)

设计思想:在对象中维护一个整数计数器变量 当有引用指向对象时,计数器加一,相反就减一(引用断开)

优点:设计实现简单,容易分辨对象是否是垃圾对象

缺点:需要维护一个变量存储引用数量,频繁修改引用计数器变量,占空间,还耗时

最重要的是,无法解决循环引用问题

可达性分析算法(根搜索算法)

设计算法:从一些可以被称为GCRoots的对象开始向下查找,只要某一个对象与GCRoots对象有联系的,可以判断对象时被使用,与根对象引用链没有任何关系的对象,可以视为垃圾对象.

哪些对象可以作为GCRoots(根对象)?

1.虚拟机占中(被调用的方法)所使用的对象

2.类中的静态属性

3.虚拟机中使用的系统类对象

Object类中有一个finalize( )方法,这个方法是在对象被回收之前,由虚拟机自动调用的,

在对象被回收前,需要执行一些操作,就可以在此方法中编写,

finalize( )方法可以在子类中重写,

finalize( )方法只会被调用一次(第一次被判定为垃圾,调用finalize(),对象有可能又被引用了,对象就不能被回收,当下一次被判定为垃圾对象时,就不会再调用finalize( ))

由于finalize( )方法存在,被标记为垃圾的对象,也不是非死不可的,

可以将对象分为三种状态:

可触及:被GCRoots引用的 不是垃圾对象

可复活的:被判定为垃圾的,但是finalize()方法还没有被调用过的

不可触及的:被判定为垃圾的,finalize( )已经被调用过了

final finally{ } finalize( )

2.垃圾回收阶段

1.标记-复制算法

将内存可以分为多个较小的块,当发生垃圾回收时,将一个区域中存活的对象复制到另一个区域,在另一个区域从头开始排列,清楚当前垃圾回收的区域.

优点:清理之后,内存没有碎片

不足:回收时,需要移动对象,所以适合小内存块,而且存活对象少的情况.

2.标记-清除算法

将被标记为垃圾的对象地址进行记录,后面如果分配新对象,判断垃圾对象空间是否能够存储下新对象.

如果能存储下,用新的对象直接覆盖垃圾对象即可.

存活对象是不发生移动的.

优点:不会移动对象的

不足:回收后,内存中会出现碎片

3.标记-整理算法(压缩)

将存储对象会移动到内存区域的一端,按顺序排列(压缩),清理边界以外的空间

在标记清除地基础上进行一次内存整理,

优点:回收后没有内存碎片

标记清除和标记压缩对比:

标记-清除:不移动存活对象,

标记-压缩:会移动存活对象,

两者都适合于老年代对象回收,

先使用标记-清除,当老年代空间不足,或者不能存储一个较大的对象时,再使用标记压缩算法.

垃圾回收时,根据不同的分区采用不同的回收算法.

新生代:标记-复制

老年代:标记-清除,标记-压缩

垃圾回收器
什么是垃圾回收器

垃圾回收器,是对垃圾回收过程实践者(落地)

不同的虚拟器中,垃圾回收器种类也是很多的

垃圾回收器分类:

从线程数量上分:

单线程 垃圾回收线程只有一个

多线程 有多个垃圾回收线程

从工作模式上分:

独占式:垃圾回收线程执行时,其他用户线程需要暂停

并发式:垃圾回收线程和用户线程可以做到并发执行

从分区角度上分:

新生代

老生代

垃圾收集器性能指标:

吞吐量

用户线程暂停时间(重点)

回收时内存开销

Serial,单线程,新生代

Serial Old,单线程 老年代

Parallel Scavenge, ParNew ,多线程新生代收集器

Parallel Old,多线程老年代收集器

CMS,多线程 老年代收集器 开创了垃圾收集线程与用户线程并发执行的先例.

并发标记清除 收集器

初始标记 ----独占执行

并发标记 ----并发执行

重新标记 ----独占执行

并发清除 ----并发执行

G1

设置垃圾回收器

G1垃圾回收器,继承了CMS中,垃圾收集线程和用户线程并行执行的特点,减少了用户线程暂停的时间

同时,将新生代和老年代的各个区域,又划分成对个更小的区域,对每个区域进行跟踪,

优先回收价值高的区域(垃圾多的区域, 例如可以把伊甸园区可以分成好几个小的区域)

提升回收效率, 提高了吞吐量. 不再区域年轻代和老年代, 可以做到对整个堆进行回收.

非常适合服务器端程序,大型项目

设置垃圾回收器

打印默认垃圾回收器

-XX:+PrintCommandLineFlags -version

打印垃圾回收详细信息

-XX:+PrintGCDetails -version

设置垃圾回收器

-XX:+UseG1GC


http://www.ppmy.cn/server/172158.html

相关文章

Github 仓库 git clone 速度过慢解决方案

很多时候想从 GitHub 上 clone 一个仓库,都会遇到速度慢的问题,而且经常连接失败,这里给出有效解决方案。 一、背景 应该是很多小伙伴碰到过的问题:想从 GitHub 上面 clone 项目,很多情况下会慢的离谱,等…

专业工具,提供多种磁盘分区方案

随着时间的推移,电脑的磁盘空间往往会越来越紧张,许多人都经历过磁盘空间不足的困扰。虽然通过清理垃圾文件可以获得一定的改善,但随着文件和软件的增多,磁盘空间仍然可能显得捉襟见肘。在这种情况下,将其他磁盘的闲置…

基于springboot+vue实现的宠物救助及领养平台(源码+L文+ppt)43-21

摘 要 宠物救助及领养平台是一个专注于宠物保护和幸福的在线平台。它致力于连接那些需要帮助的宠物与愿意给予它们关爱的家庭。通过这个平台,人们可以报告丢失的宠物、寻求救助资源,以及浏览可领养的宠物信息。该平台不仅提供了一个渠道,让…

【C++经典例题】回文串判断:两种高效解法剖析

💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:C经典例题 期待您的关注 目录 一、问题描述 示例 二、解法一:将字母数字连接到新的 string 思路 代码实现 代码解…

【R包】pathlinkR转录组数据分析和可视化利器

介绍 通常情况下,基因表达研究如微阵列和RNA-Seq会产生数百到数千个差异表达基因(deg)。理解如此庞大的数据集的生物学意义变得非常困难,尤其是在分析多个条件和比较的情况下。该软件包利用途径富集和蛋白-蛋白相互作用网络&…

Nginx+PHP+MYSQL-Ubuntu在线安装

在 Ubuntu 上配置 Nginx、PHP 和 MySQL 的步骤如下: 1. 更新系统包 首先,确保系统包是最新的: sudo apt update sudo apt upgrade2. 安装 Nginx 安装 Nginx: sudo apt install nginx启动并启用 Nginx 服务: sudo…

Docker项目部署-部署Java应用

总结 部署一个Java项目需要做什么事情。 1.首先需要将项目打包,打包完得到jar包。 2.把打包得到的jar包和Dockerfile一起放到虚拟机里。 3.利用命令docker build -t 镜像名 . 构建镜像。 4.最后利用docker run 去部署应用。

JavaFunction的使用

一、基础概念与核心方法 ​定义与作用​ Function<T, R> 是一个函数式接口&#xff0c;接收类型为 T 的输入参数&#xff0c;返回类型为 R 的结果。其核心方法为 apply(T t)。例如&#xff0c;将字符串转换为整数长度&#xff1a; java Function<String, Integer>…