JVM:从零到入门

JVM,就是Java虚拟机。

JVM是一个巨大的话题,我们本文主要简单介绍一些围绕JVM相关的基础知识。

目录

JVM内存区域划分

本地方法栈

虚拟机栈

程序计数器

方法区/ 元数据区

类加载

1.加载

2.验证

3.准备

4.解析

5.初始化

双亲委派模型

垃圾回收机制

引用计数

可达性分析

如何清除垃圾

标记清除

复制算法

标记整理


JVM内存区域划分

JVM启动的时候会申请到一个很大的内存区域,根据需要,把整个空间分成好几个部分。

本地方法栈

native就表示JVM内部的C++代码,这个部分就是给调用native方法(JVM内部的方法)准备的空间。

虚拟机栈

JVM Stacks 是JVM中一个特定的空间,这里储存的是方法之间的调用关系。(前面的本地方法栈储存的就是native之间的调用关系)

Heap,是整个JVM中最大的区域。此内存区域的唯一目的就是存放对象实例,如类的成员变量等等。在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。

程序计数器

对于线程来说,在cpu上运行的时候如果cpu被调度走了,那么怎么保证cpu回来的时候线程还是当前进度呢?程序计数器就是干的这个活,记录当前线程执行到哪个指令。为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储。

方法区/ 元数据区

Java8开始改名,以前叫做方法区,现在叫元数据区。

它用于存储已被Java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

同时要注意,只有元数据区和堆上的数据是多线程共享的,其他的都是每个线程独立的数据。

局部变量在栈上,普通成员变量在堆上,静态成员变量在元数据区。

类加载

程序员编写一个.java文件后,javac把这个.java文件编译成.class文件,然后再通过JVM中的类加载,从硬盘被加载到内存中的过程。

1.加载

在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:

1)通过一个类的全限定名来获取定义此类的二进制字节流。

2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

也就是把文件读到内存中的步骤。

2.验证

验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节 流中包含的信息符合《Java虚拟机 规范》的全部约束要求,保证这些信 息被当作代码运行后不会危害虚拟机自身的安全。

比如:文件格式验证 字节码验证 符号引用验证

3.准备

准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。

例如:

private static int value = 123;

此时就是初始化value的int值为0,而不是123。(相当于在元数据区先占个位置)

4.解析

解析阶段是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。

5.初始化

初始化阶段,Java 虚拟机真正开始执行类中编写的 Java 程序代码,将主导权移交给应用程序。初始化 阶段就是执行类构造器方法的过程。

那么一个类什么时候会被加载呢?

不是Java程序一运行就会把所有的类都加载,而是真正用到了才会加载。(懒汉模式)

1.构造类的实例

2.调用类的静态方法、使用静态属性

3.加载子类,就会先加载其父类

双亲委派模型

加载是把.class文件找到,读取文件内容的过程,双亲委派模型就是描述这个加载,找.class文件的基本过程。

JVM默认提供了三个类加载器:

上述三个类,存在parent属性,指向自己的父类加载器。

加载一个类的时候,先从ApplicationClassLoader开始的,但是它会把加载任务交给其父亲ExtensionClassLoader,让其父亲去执行。于是ExtensionClassLoader要去加载了,但是也不是真的加载,而是把加载任务再委托给自己的父亲。它的父亲BootstrapClassLoader要去加载了,但是发现自己的父亲是null,没有父亲,于是只能自己加载。

等待自己加载完后,如果找到了就加载,没有找到就由自己的子类加载器进行加载。也就是BootstrapClassLoader加载完没找到后,让ExtensionClassLoader加载,如果也没找到最后再让ApplicationClassLoader加载。

最后ApplicationClassLoader如果找到了就加载,没找到就由子类加载器进行加载。由于当前没有子类了,就只能抛出类找不到这样的异常。

为什么要有上述顺序?这套顺序出自JVM实现代码的逻辑,并且这段代码大概是类似于递归的方式写的。这个顺序最主要的目的就是为了保证Bootstrap能够先加载,Application后加载,这样能够解决用户自己创建了一些奇怪的类引起bug。

例如用户自己写了一个java.lang.String这样的类,按照上述加载流程,JVM此时加载道德其实是标准库的类,而不会加载到用户自己的这个类。

同时用户自己也可以定义类加载器,可以和现有的配合使用。

垃圾回收机制

我们在java开发中何时考虑过内存管理?不像c和c++还要考虑什么时候释放资源,java只需要考虑业务实现就行了。

什么是垃圾呢?垃圾指的是不再使用的内存,垃圾回收就是把不用的内存帮我们自动释放了。

在别的语言中,如果不手动释放垃圾,这块内存空间就会一直存在,一直存在到进程结束。这就可能导致内存泄漏。剩余的空间越来越少,进一步导致后续的内存申请操作失败!

GC是垃圾回收中最主流的一种方式。很多语言都使用GC来解决上述问题。

JVM中有很多内存区域,GC主要是针对堆进行释放的。

同时GC是以对象为基本单位进行回收的,而不是字节。要回收就是回收整个对象,而不是回收半个对象。

GC回收的是对象,那么就要看有没有引用指向它。

如何知道有没有引用指向呢?有两种方式:

1.引用计数

2.可达性分析

引用计数

引用计数中,给每个对象都分配了一个计数器(整数),每次创建一个引用指向这个对象计数器就+1,这个引用被销毁了计数器就-1。

这个办法简单有效,但是Java并没有使用。因为:

1.内存空间浪费很严重,一个对象就要分配一个计数器。

2.存在循环引用的问题。

所以在Java中不用引用计数这种方式。

可达性分析

Java中的对象都是通过引用来指向并访问的。所以整个Java中的所有对象类似于一个树根节点,通过链式或者树形结构一个个串连起来。

可达性分析就是把这些对象被组织的结构看做树,从树根节点出发,遍历树,所有能被访问到的对象标记成“可达”。

通过这种方式,只需要有一个GC Roots节点就可以访问到所有的元素。访问不到的就代表着可以被GC回收了。所有的对象在创建的时候JVM都会记录下地址。

1、虚拟机栈(栈帧中的本地变量表)中引用的对象
2、方法区中类静态属于引用的对象
3、方法区中常量引用的对象
4、本地方法栈中Native方法引用的对象。

如何清除垃圾

主要有三种方式:标记清除,复制算法,标记整理。

标记清除

在一块内存空间中,2,4,6是需要清楚的垃圾,那么直接对其垃圾回收就可以完成。但是这样做就会有一个问题:全都是内存碎片。被释放的空闲空间是零散的,不是连续的。如果要申请一块很大的空间,必须要是连续的空间,此时就会申请失败!

复制算法

复制算法会把内存分成两半,对于垃圾(1和3)不去处理,把不是垃圾的部分(2和4)复制到另一边。

此时再把左边的内存清除。这样也就是把需要回收的垃圾处理掉了。

这种方法空间利用率低,并且垃圾少、有效对象多那么复制成本就比较大。

标记整理

标记整理解决了复制算法的缺点,类似于顺序表删除中间元素的操作,并且会有元素搬运的操作。

1 3 5 是垃圾,在标记整理的时候,会把1 3 5删除,然后把2移动到1的位置,4移动到2的位置,6移动到3的位置。

这种方式保证了空间利用率,并且解决了内存碎片问题。

但是这种方式效率也不高,如果搬运的空间比较大开销也会很大。

实际上基于这一些基本的策略,把垃圾回收分成不同的场景,不同的场景有不同的算法。这就是“分代回收”。

这个“代”,就是一代代的代。我们给对象引入一个概念:年龄。这个年龄,是熬过GC的轮次。如果经历了一轮可达性分析,发现这个对象还不是垃圾,就说明这个对象熬过了一轮GC。

JVM把堆分成两个区域:新生代、老年代。

新生代又分为Eden、From Survivor、To Survivor。

  1. 生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。

  2. Edem : From Survivor : To Survivor = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,From Survivor = To Survivor = 1/10 的新生代空间大小。

数据会首先分配到Eden区当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。当Eden没有足够空间的时候就会触发jvm发起一次Minor GC,。如果对象经过一次Minor-GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代中了,当然晋升老年代的年龄是可以设置的。

这也就是之前的三种垃圾回收机制的分代回收中的代。其实主要原因就是可以根据各个年代的特点进行对象分区存储,更便于回收,采用最适当的收集算法:

新生代中,每次垃圾收集时都发现大批对象死去,只有少量对象存活,便采用了复制算法,只需要付出少量存活对象的复制成本就可以完成收集。(从From Survivor到To Survivor用的是复制算法)

而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须采用标记清除或者标记整理算法。

到此为止,JavaEE初阶的内容告一段落~接下来博主会持续更新JavaEE进阶一些的内容,快快关注吧~


 

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

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

相关文章

yydict属性字典-一种更加方便的方式访问字典

yydict属性字典-一种更加方便的方式访问字典 问题引入 这篇文章是想介绍 最近在使用字典的一种困惑. 我希望通过少写几个字符来访问 python中字典这种数据结构. 比如这个例子: person {name: frank,age: 18,hobby: swimming }在python中字典的定义 如上面的例子, 如果我希…

描述 power iteration(幂法)是啥?

幂法(Power Iteration)是一种迭代算法,用于计算一个矩阵的最大特征值和对应的特征向量。它是特征值求解问题中常用的一种方法。 幂法基于以下观察:如果一个矩阵 A 的某个特征向量 x 对应的特征值 λ 是最大的,那么当将…

spring boot mybatis plus mapper如何自动注册到spring bean容器

##Import(AutoConfiguredMapperScannerRegistrar.class) ##注册MapperScannerConfigurer ##MapperScannerConfigurer.postProcessBeanDefinitionRegistry方法扫描注册mapper ##找到mapper候选者 ##过滤mapper 类 候选者 ##BeanDefinitionHolder注册到spring 容器

vue项目之.env文件.env.dev、test、pro

.env文件是vue运行项目时的环境配置文件。 .env: 全局默认配置文件,所有环境(开发、测试、生产等)均会加载并合并该文件 .env.development(开发环境默认命名) 开发环境的配置,文件名默认为.env.development,如果需要改名也是可以的&#xf…

安卓主板_MTK联发科低功耗迷你安卓核心板开发板定制开发

卓越V100安卓主板 是新移科技推出的4G物联网行业通用主板。卓越V100安卓主板基于 联发科MT6761安卓核心板设计,支持运行Android9.0 系统,通用性强,有很好的人机交互性,可进行二次开发,有良好的移植性。 另外&#xff…

面试经典150题(82-83)

leetcode 150道题 计划花两个月时候刷完,今天(第四十一天)完成了2道(82-83)150: 82.(133. 克隆图)题目描述: 给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝&#xff08…

ZZULIOJ 1118: 数列有序

题目描述 一个非递减有序的整型数组有n个元素,给定一个整数num,将num插入该序列的适当位置,使序列仍保持非递减有序。 要求定义一个函数insert(),将整数num插入在数组a的适当位置上,函数原型如下: void …

面向工业的复合自动机器学习

一、工业数据收集与处理 在面向工业的复合自动机器学习中,数据的收集和处理是至关重要的第一步。工业数据通常具有大规模、高维度和非线性的特点,需要运用先进的数据处理技术进行有效的管理和分析。这包括数据清洗、数据预处理、数据增强等一系列操作&a…

【Python】使用pyinstaller打包为Windows平台的xxx.exe方法步骤

pyinstaller 是一个用于将 Python 代码打包成独立可执行文件的工具,它可以将 Python 代码打包成 Windows、Linux、Mac 等平台的可执行文件,方便用户在不同环境中运行。 pyinstaller用法: 1.安装pyinstaller库,这里以PyCharm环境为…

ZZULIOJ 1113: 递归调用的次数统计(函数专题)

题目描述 如下程序的功能是计算 Fibonacci数列的第n项。函数fib()是一个递归函数。请你改写该程序&#xff0c;计算第n项的同时&#xff0c;统计调用了多少次函数fib&#xff08;包括main()对fib()的调用&#xff09;。 #include<stdio.h> int fib(int k); int main() {…

ACM论文LaTeX模板解析(一)| 模板下载与安装

本文收录于专栏&#xff1a;ACM 论文 LaTeX模板解析&#xff0c;本专栏将会围绕ACM 论文 LaTeX模板解析持续更新。欢迎点赞收藏关注&#xff01; 文章目录 1. 引言2. 下载方式 1. 引言 计算机械协会&#xff08;ACM&#xff0c;Association for Computing Machinery&#xff0…

封装一个ajax请求

这段代码定义了一个名为ajaxRequest的函数&#xff0c;接收三个参数&#xff1a;URL、HTTP方法&#xff08;可选&#xff09;和要发送的数据。该函数会创建一个XMLHttpRequest对象并进行相应配置&#xff0c;然后根据传入的参数发起网络请求。最后通过Promise来处理请求成功或失…

vue3+vant+cropper.js实现移动端图片裁剪功能

一、前言 最近做项目中遇到一个需求&#xff0c;需要对海报图片按照一定的比例进行裁剪并上传到oss。一开始这个需求思路有两个&#xff0c;使用canvas原生或者寻找现成的第三方库&#xff0c;对比了一番觉得canvas实现时间耗费较长&#xff0c;且秉承着不重复造轮子的原则&am…

docker镜像分层

Docker 镜像是由一系列分层&#xff08;layers&#xff09;组成的。每一层都包含了文件系统的一部分或一个操作指令。这种分层结构使得镜像的构建、分发和存储变得更加高效。当你构建一个新的镜像时&#xff0c;Docker 会尽可能地重用已经存在的层&#xff0c;从而减少重复工作…

UG装配-WAVE几何链接器

自上向下&#xff08;自顶向下&#xff09;设计 先将产品主要结构&#xff08;或主要部件&#xff09;建立好&#xff0c;然后再根据要求设计其它组件&#xff0c;使每个组件之间有数据关联&#xff0c;适用于产品开发初期&#xff0c;便于修改&#xff0c;修改组件数据后&…

基于JavaWeb的酒店管理系统

基于JavaWeb的酒店管理系统 文章目录 基于JavaWeb的酒店管理系统系统介绍技术选型成果展示源码获取账号地址及其他说明 系统介绍 基于JavaWeb的酒店管理系统是为酒店打造的管理平台&#xff0c;其主要功能有管理员登陆、客房预订、客房入住、房间管理、数据查询(预订单查询、入…

PySide6/PyQt6中的时间管理类:QTime的使用方法

文章目录 📖 介绍 📖🏡 环境 🏡📒 使用方法 📒📝 创建QTime对象📝 常用方法⚓️ 相关链接 ⚓️📖 介绍 📖 QTime是PySide6中用于处理时间段的类,可以用来表示一天中的时间,例如小时、分钟和秒。它提供了许多操作和格式化时间的功能,使得处理时间变得更加…

mysql-实战案例 (超详细版)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

VL53L4CD TOF开发(1)----驱动TOF进行测距

VL53L4CX TOF开发.1--驱动TOF进行测距 概述视频教学样品申请完整代码下载主要特点硬件准备技术规格系统框图应用示意图生成STM32CUBEMX选择MCU串口配置IIC配置 XSHUTX-CUBE-TOF1演示结果 概述 VL53L4CD适用于接近测量和短距离测量&#xff0c;可实现从仅仅1 mm到1300 mm的超精…

SAP SD-DN-MM 交货单相关物料凭证的视图的日期问题

眼下有个需求 获取交货单对应的物料凭证的过账日期BLDAT。 同步BW数据过去 新增一个数据库视图 但是实际使用时&#xff0c;有效部分仅本月&#xff0c;再选择条件里面要加上 MATdoc-bldat > sy-datum - sydatum6(2). 于是使用ST05 跟踪了一下&#xff0c;发现在DD28S…