JVM笔记(JVM内存+垃圾回收器)详解

一:java代码的执行流程(引出JVM)

  • 首先由程序员编写成.java文件
  • 然后由javac(java编辑器)将.java文件编译成.class文件
  • .class文件可以在不同平台/操作系统上的JVM上执行
  • 再由JVM编译成可供不同操作系统识别的机器码(0,1二进制)
    在这里插入图片描述

二:JVM来源

我们在下载JDK的时候,也就把JVM给下载下来了 (因为JDK包含JRE,JRE包含JVM)

1:JRE: Java Runtime Environment

JRE顾名思义是java运行时环境,包含了java虚拟机,java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的。

2:JDK:Java Development Kit

顾名思义是java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。

3.JVM虚拟机

JVM(JAVA虚拟机)是运行Java字节码的虚拟机,通过编译.java文件为.class文件得到字节码文件 . .class文件包含JVM可以理解的字节码。
在现实世界中,JVM是一种规范,它提供可以执行Java字节码的运行时环境。
如果你需要运行java程序,只需安装JRE就可以了。如果你需要编写java程序,需要安装JDK。

在这里插入图片描述

三:JVM在Java程序运行当中的位置

JVM之所以称为虚拟机,是因为它提供了一个不依赖于底层操作系统和机器硬件体系结构的机器接口。这种与硬件和操作系统的独立性使得Java程序“写一次,到处运行”(write-once-run-anywhere).
在这里插入图片描述

四:JVM的体系结构

1:图示

那么我们分层来解析这个体系结构的重要的部分
在这里插入图片描述

2:类加载器系统

(1):类加载机制

a:首先要知道 java程序运行的几个阶段

在这里插入图片描述

b:累加载机制

  • 类加载顾名思义就是把类加载到 JVM 中,而输出一段二进制流到内存,之后经过一番解析、处理转化成可用的 class 对象,这就是类加载要做的事情。

  • 二进制流可以来源于 class 文件,或者通过字节码工具生成的字节码或者来自于网络都行,只要符合格式的二进制流,JVM 来者不拒

  • 在这里插入图片描述

  • 类加载流程分为加载、连接、初始化三个阶段,连接还能拆分为:验证、准备、解析三个阶段。

  • 加载:JVM在该阶段的主要目的使将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至使网络)转化为二进制字 节流加载到内存中方法区中,并在内存中生成一个代表该类的Class对象

  • 验证:主要是验证加载进来的二进制流是否符合一定格式,是否规范,是否符合当前 JVM 版本等等之类的验证。

  • 准备:为静态变量(类变量)赋初始值,也即为它们在方法区划分内存空间。这里注意是静态变量,并且是初始值,比如 int 的初始值是 0。

  • 解析:将常量池的符号引用转化成直接引用。符号引用可以理解为只是个替代的标签,比如你此时要做一个计划,暂时还没有人选,你设定了个 A 去做这个事。然后等计划真的要落地的时候肯定要找到确定的人选,到时候就是小明去做一件事。 解析就是把 A(符号引用) 替换成小明(直接引用)。符号引用就是一个字面量,没有什么实质性的意义,只是一个代表。直接引用指的是一个真实引用,在内存中可以通过这个引用查找到目标。

  • 初始化:这时候就执行一些静态代码块,为静态变量赋值,这里的赋值才是代码里面的赋值,准备阶段只是设置初始值占个坑。

c:类加载的补充

那么这个类加载中 加载阶段 我们从不同的数据源找这个.class文件是如何找到的呢?

-加载类,JVM有三种类加载方式:Bootstrap(启动类加载器),extension(扩展类加载器),application(系统类加载器)
-当加载类文件时,JVM会找到某个任意类XYZ.class的依赖项。

  • 第一个启动类载入器试图查找类,它会扫描lib文件夹下的rt.jar文件
  • 如果没有找到类,则extension类加载器会在jre\lib\ext文件夹下查找该类
  • 同样没有找到类,application类加载器会在系统CLASSPATH环境变量中查询所有的Jar文件和类.
  • 如果类被任何加载器发现,则被类加载器载入,否则抛出异常:ClassNotFoundException。

(2):类加载器

首先这个说到类加载器肯定是要讲到双亲委派模型的

  • 类加载器:讲到类加载不得不讲到类加载的顺序和类加载器。Java 中大概有四种类加载器,分别是:启动类加载器(Bootstrap ClassLoader),扩展类加载器(Extension ClassLoader),系统类加载器(System ClassLoader),自定义类加载器(Custom ClassLoader),依次属于继承关系(注意这里的继承不是 Java 类里面的 extends)
  • 启动类加载器(Bootstrap ClassLoader):主要负责加载存放在Java_Home/jre/lib下,或被-Xbootclasspath参数指定的路径下的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载),启动类加载器是无法被Java程序直接引用的。
  • 扩展类加载器(Extension ClassLoader):主要负责加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载Java_Home/jre/lib/ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。
  • 系统类加载器(System ClassLoader):主要负责加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
  • 自定义类加载器(Custom ClassLoader:自己开发的类加载器
  • 双亲委派模型不是一种强制性约束,也就是你不这么做也不会报错怎样的,它是一种JAVA设计者推荐使用类加载器的方式。
  • 在这里插入图片描述
    为甚要双亲委派
    其实就是规范,当我们加载这个.Class字节码文件的时候,是从本地磁盘中找这个文件的,那么入如果有在不同文件目录下的相同的.Class文件的时候 那就该如何选择呢,这时就需要一定的规范的和准则了。
    它使得类有了层次的划分。就拿 java.lang.Object 来说,加载它经过一层层委托最终是由Bootstrap ClassLoader来加载的,也就是最终都是由Bootstrap ClassLoader去找<JAVA_HOME>\lib中rt.jar里面的java.lang.Object加载到JVM中。
    这样如果有不法分子自己造了个java.lang.Object,里面嵌了不好的代码,如果我们是按照双亲委派模型来实现的话,最终加载到JVM中的只会是我们rt.jar里面的东西,也就是这些核心的基础类代码得到了保护。
    因为这个机制使得系统中只会出现一个java.lang.Object。不会乱套了。你想想如果我们JVM里面有两个Object,那岂不是天下大乱了。

3:JVM的内存空间

(1):前言

主要是考虑jvm的内存和实际物理内存的关系,首先可以知道的jvm(虚拟机)的内存模型是虚拟出来的模仿实际操作系统内存的,但我们new 出来一个对象是占用虚拟机的内存,同时也占用物理内存(虚拟机中的内存是与计算机的物理内存映射的)。

(2):JVM的体系结构图

在这里插入图片描述

(3):操作系统的内存布局

a:前言

为了解JVM的体系结构图,我们非常有必要先了解一下操作系统的内存基本结构

b:操作系统的内存布局

在这里插入图片描述

c:操作系统的JVM

在这里插入图片描述
为什么jvm的内存是分布在操作系统的堆中呢??因为操作系统的栈是操作系统管理的,它随时会被回收,所以如果jvm放在栈中,那java的一个new的对象就很难确定会被谁回收了,那gc的存在就一点意义都没有了,而要对栈做到自动释放也是jvm需要考虑的,所以放在堆中就最合适不过了。

d:操作系统+jvm的内存简单布局

在这里插入图片描述

  • 从这个图,你应该不难发现,原来jvm的设计的模型其实就是操作系统的模型,
  • **基于操作系统的角度,jvm就是个该死的java.exe/javaw.exe,也就是一个应用,用来加载.class文件
  • 而基于class文件来说,jvm就是个操作系统,**
  • 而jvm的方法区,也就相当于操作系统的硬盘区,
  • 而java栈和操作系统栈是一致的,无论是生长方向还是管理的方式,
  • 至于堆嘛,虽然概念上一致目标也一致,分配内存的方式也一直(new,或者malloc等等),但是由于他们的管理方式不同,jvm是gc回收,而操作系统是程序员手动释放,所以在算法上有很多的差异,gc的回收算法。

在这里插入图片描述

e:操作系统+jvm的内存简单布局+PC寄存器

在这里插入图片描述
将这个图和上面的图对比多了什么?没错,多了一个pc寄存器,我为什么要画出来,主要是要告诉你,所谓pc寄存器,无论是在虚拟机中还是在我们虚拟机所寄宿的操作系统中功能目的是一致的,计算机上的pc寄存器是计算机上的硬件,本来就是属于计算机,(这一点对于学过汇编的同学应该很容易理解,有很多的寄存器eax,esp之类的32位寄存器,jvm里的寄存器就相当于汇编里的esp寄存器),计算机用pc寄存器来存放“伪指令”或地址,而相对于虚拟机,pc寄存器它表现为一块内存(一个字长,虚拟机要求字长最小为32位),虚拟机的pc寄存器的功能也是存放伪指令,更确切的说存放的是将要执行指令的地址,它甚至可以是操作系统指令的本地地址,当虚拟机正在执行的方法是一个本地方法的时候,jvm的pc寄存器存储的值是undefined,所以你现在应该很明确的知道,虚拟机的pc寄存器是用于存放下一条将要执行的指令的地址(字节码流)。

f:操作系统+jvm的内存简单布局+PC寄存器+classLoader

在这里插入图片描述

  • 这个图是要告诉你,当一个classLoder启动的时候,classLoader的生存地点在jvm的堆,
  • 然后它会去主机硬盘上将A.class装载到jvm的方法区,
  • 方法区中的这个字节文件会被虚拟机拿来new A字节码(),然后在堆内存生成了一个A字节码的对象,
  • 然后A字节码这个内存文件有两个引用一个指向A的Class对象,一个指向加载自classLoader
  • 那么这个字节码文件中还有什么信息呢
    在这里插入图片描述
    补充:
    这里的A.class文件存放的位置 主机磁盘,就是我们类加载阶段中的加载 通过不同的类加载器
    在主机不同的文件夹中找.class

(4):JVM内存中的栈和堆

a:Java内存分配中的栈

在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

b:Java内存分配中的堆

  • 堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

  • 在堆中产生了一个数组或对象后,还可以 在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。

  • 引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍 然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。

  • 实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针!

  • 在这里插入图片描述

4:JVM的垃圾回收器

JVM笔记之垃圾回收器

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

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

相关文章

跟我一起学.NetCore之Asp.NetCore中集成Autofac扩展

前言前两节针对.NetCore自带的依赖注入进行简要概述&#xff0c;对于日常开发的需求应该是能满足了&#xff0c;那为什么还需要引入第三方依赖注入组件呢&#xff0c;这里就从自带的依赖注入来分析&#xff0c;有什么样的需求满足不了&#xff1f;主要归纳为以下几点&#xff1…

C++ 学习之旅(7)——指针pointer

开门见山&#xff0c;如果把计算机的内存空间比作是一排房子&#xff0c;那指针就是房门号。指针实际上就是一个用来存储内存地址的整数&#xff0c;与类型没有关系&#xff0c;我可以定义一个void类型的指针&#xff1a; #include <iostream>int main() {int var 8;v…

leetcode509. 斐波那契数

一:论语 我现在应该还没到壮年 还在年少 应该。。。。。。。。。。。。。。。 二:题目 三:上码 class Solution { public:/**思路:动态规划5步曲1.确定dp数组以及下标的含义dp[i] 的定义为:第i个斐波那契数的数值是dp[i]2.确定递推公式状态转移方程 dp[i] dp[i-1] dp[i-2…

C++ 学习之旅(8)——一文搞懂指针、引用、函数参数的传值调用、指针调用和引用调用

废话少说&#xff0c;直接上代码&#xff1a; #include <iostream>int main() {int a 5;int* ptr &a;int& ref a;std::cin.get(); //设置断点 }为了避免混淆&#xff0c;我建议在定义指针时写int* ptr而不是int *ptr&#xff0c;同理&#xff0c;定义引用写…

.NET Core 下使用 Kafka

安装CentOS 安装 kafkaKafka : http://kafka.apache.org/downloadsZooLeeper : https://zookeeper.apache.org/releases.html下载并解压# 下载&#xff0c;并解压 $ wget https://archive.apache.org/dist/kafka/2.1.1/kafka_2.12-2.1.1.tgz $ tar -zxvf kafka_2.12-2.1.1.tgz…

leetcode70. 爬楼梯

一:题目 二:上码 class Solution { public:/**思路&#xff1a;分析题意:爬到第一层楼有一种方法,爬到第二层楼有两种方法那么由第一层到第三层需要跨2步,由第二层到第三层需要跨一步;那么到第三层的方法可以由 到第一层和第二层推导出来(因为只剩下最后一步了)动态规划五步走…

发现一款.NET Core开源爬虫神器:DotnetSpider

没有爬虫就没有互联网&#xff01;爬虫的意义在于采集大批量数据&#xff0c;然后基于此进行加工/分析&#xff0c;做更有意义的事情。谷歌&#xff0c;百度&#xff0c;今日头条&#xff0c;天眼查都离不开爬虫。去开源中国和Github查询C#的爬虫项目&#xff0c;仅有几个非常简…

leetcode746. 使用最小花费爬楼梯

一:题目 二:上码 class Solution { public:/**思路:1.分析题意给出的数组的下标代表楼梯的台阶数2.动态规划五步走1>:确定dp数组以及下标的含义dp[i]:表示到达第i层所需要花费的体力2>:确定dp数组的递推公式那么如何得到dp[i](花费的体力)呢&#xff1f;dp[i]由dp[i-1]或…

SS CMS 全新跨平台 V7.0 版本正式发布

今天&#xff0c;我们很高兴宣布基于.NET CORE平台的全新 SS CMS V7.0正式发布&#xff0c;新版本采用.NET CORE模块化和高性能实现&#xff0c;用于创建在Windows&#xff0c;Linux、Mac以及Docker上运行的Web应用程序和服务。SS CMS 7.0 之旅在此&#xff0c;我们简单回顾一下…

leetcode62. 不同路径

一:题目 二:上码 class Solution { public:/**思路:1.分析题意:2.动态规划五步走:1>:确定dp数组和其下标的含义dp[i][j]为到达二维数组下标为i&#xff0c;j的路径条数,i和j为下标2>:确定dp数组的递推公式那么dp[i][j]是如何求解出来的呢?只能是两个方向左边:dp[i-1][j…

推荐一本基于ASP.NET Core 3.1的实战来了

第一本基于 ASP.NET Core 3.1 的实战书来了我脱产花费了一年时间创作书籍《深入浅出 ASP.NET Core》&#xff0c;终于上架了。目前天猫、京东等主流平台均有销售。这本书是基于.NET Core3.1 平台&#xff0c;从 ASP.NET Core 的基础入门,通过项目实战结合 ASP.NET Core 源代码解…

leetcode63. 不同路径 II

一:题目 二&#xff1a;上码 class Solution { public:/**思路:1.分析题意:这里有障碍物,所以我们的做法会有点变化&#xff0c;如果这个障碍物出现在左侧或则右测的话那么我们确定的是其障碍物包括其后面的下标将均为0,如果障碍物出现在上诉区域外的话&#xff0c;那么就直接…

C#刷剑指Offer | 从上到下打印二叉树

【C#刷题】| 作者 / Edison Zhou这是EdisonTalk的第288篇原创内容我们来用之前学到的数据结构知识来刷《剑指Offer》的一些核心题目&#xff08;精选了其中30道题目&#xff09;&#xff0c;希望对你有帮助&#xff01;本文题目为&#xff1a;从上到下打印二叉树。1题目介绍题目…

跟我一起学.NetCore之自定义配置源-热更新-对象绑定

前言上一篇针对不同的配置源进行举例演示&#xff0c;感受到不同配置源和不同数据格式使用统一操作的便捷(即没有什么加一层解决不了的&#xff0c;这个不是我说的)&#xff0c;这里接着说说自定义配置源、配置热更新、配置绑定对象相关操作&#xff1b;配置源回顾&#xff1a;…

leetcode343. 整数拆分(思路+详解)

一:题目 二:上码 class Solution { public:/**思路:1.分析题意:将一个数拆分为几个数相加的和 然后求取这几个数相乘的最大积,这个就很动态规划也就是我们可以得到多种结果&#xff0c;要在多种结果中取最优2.动态规划:1>:确定dp数组代表啥&#xff0c;以及下标的含义dp[i…

C++ 学习之旅(14)——构造函数constructors和析构函数destructors

首先我们看下一个简单的类&#xff1a; #include <iostream>class Entity { public:float X, Y;void Print(){std::cout << X << "," << Y << std::endl;} };int main() {Entity e;e.Print();std::cin.get(); }输出结果如下&#xf…

跟我一起学.NetCore之配置变更监听

前言通常程序中配置少不了&#xff0c;配置的修改也避免不了&#xff0c;配置的热更新为此给应用程序带来很大的便捷&#xff0c;不用重启&#xff0c;提高用户体验&#xff1b;但往往有时候需要对修改进行审计&#xff0c;也就是需要记录&#xff0c;有时候也会针对配置修改的…

解决Spring boot整合mybatis,xml资源文件放置及路径配置问题

一:问题描述 1:前言 无论你是将mapper.xml文件是和resources建造在一块&#xff0c;还是将mapper.xml文件和mapper放在一块,我们只要修改在yaml当中的mapper-locations的相对路径即可。&#xff08;前提是你在pom文件中导入了相关的resources路径&#xff09; 2:下方是将map…

C++ 学习之旅(15)——继承inheritance

所谓继承&#xff0c;就是在原有的类的基础上&#xff0c;通过继承它并添加一些新的成员&#xff0c;从而产生出一个新的类。例如我们在游戏有实体Entity和玩家Player&#xff0c;它们都有位置X&#xff0c;Y和一个移动的方法Move&#xff0c;但不同的是玩家有名字Name&#xf…

dotNET Core:编码规范

在项目开发过程中&#xff0c;由于时间紧、任务重&#xff0c;很容易导致面向功能编程。实现相同的功能&#xff0c;代码可以写的很优雅&#xff0c;也可以写的很晦涩和复杂。现在的工作&#xff0c;都需要进行团队协作&#xff0c;代码就需要有一定的规范进行指引&#xff0c;…