JVM java虚拟机深度学习

一、简介

1.介绍

Java虚拟机Java Virtual Machine,JVM)是Java平台的核心组成部分之一,它是一个在计算机上运行Java字节码的虚拟机。JVM 充当了 Java 应用程序和底层操作系统之间的中间层,提供了跨平台的特性,使得 Java 程序可以在不同的操作系统和硬件上运行。

2.JVM的主要功能和特点

(1)主要功能

        执行字节码:JVM能够解释和执行Java编译后生成的字节码class文件。字节码是一种中间语言,它类似于机器码但不直接依赖于具体的硬件和操作系统,而是依赖于JVM来执行。

        内存管理:JVM负责动态分配和管理Java程序的内存。它提供了垃圾回收机制(Garbage Collection)来自动释放不再使用的内存。

        即时编译:JVM使用即时编译器Just-In-Time Compiler,JIT)来优化字节码的执行速度。即时编译器将热点代码(即被频繁执行的代码)编译为本地机器码,提高执行效率。

        异常处理:JVM提供了异常处理机制。此机制可捕获和处理Java程序中的异常。通过异常表和异常处理器来管理异常的传播和处理流程,保证程序的稳定性和可靠性。

        类加载和运行时环境:JVM负责加载、验证、链接和初始化Java类。它使用类加载器(Class Loader)将类文件加载到内存中,并在运行时创建和管理类的实例。JVM还提供了一些系统级的库和API,用于支持Java程序的运行。

(2)特点

        跨平台:跨平台性是JVM的是Java语言的重要优势之一。例如经常使用的Linux系统,MacOS系统,Win系统,一次编译,处处运行就是这样理解的。

        字节码:字节码文件即JVM可以识别并执行的二进制文件,不同的编程语言经过编译器编译处理之后,转换成统一的字节码规范文件,这样JVM就可以执行。

        跨语言:随着JVM不断的发展和优化,很多语言都使用了JVM的能力。

 总的来说,JVM提供了Java程序运行的环境和基础设施,为开发者提供了一种跨平台、高效、安全的开发和运行环境。

二、java虚拟机

1.JVM整体构成

 本地方法栈:非java写的方法(即native方法)执行时,存放于此;

虚拟机栈:每执行一个方法,都会在这里创建一个栈帧,存放操作数栈、常量表、返回值地址信息;

方法区:类加载子系统将class加载到内存中,即放在方法区,字节码指令存放在这里

程序计数器:线程间的切换原因,线程每次执行后都要记录该线程执行的地址,以便下次执行是从上次执行位置向下执行,即记录线程下次要执行的地址;

堆:存放java对象

类加载子系统:将编译后的java代码(class)文件从磁盘中加载到内存中;

解释器:用来执行方法区中存放的字节码指令;

JIT 编译器:即时编译,解释器执行字节码指令时,有些指令是多次重复执行,针对这些热点指令,翻译一次后直接缓存,不用每次都翻译,提高执行效率,因为字节码指令会由解释器翻译为机器指令,每次执行都会翻译;缓存后每次执行这个指令,就会从缓存中获取这些字节码指令的机器指令;

垃圾回收器:回收垃圾对象;

本地方法:其他语言写的方法,即native方法

1.1 类加载子系统

 1.1.1 类加载子系统介绍

类加载子系统是将class文件加载到方法区的内存中,然后对class文件进行验证,class文件是否正确,是否被修改过,格式是否正确;之后进行准备操作,准备操作有两个不走,一是为static变量分配内存,二是给变量赋值为零值,例如 int a = 10,那么准备工作会将变量赋值为 int a = 0,在初始化步骤中将a 再赋值为10;

解析: 解析(Resolution)是将符号引用解析为直接引用的过程。为了理解这个过程,我们首先需要了解符号引用和直接引用的概念。

        符号引用(Symbolic Reference):是一种编译时期的引用,他使用符号来表示所引用的目标。比如类、方法、字段等,符号引用并不直接指向内存中的位置,而是通过符号的形式描述所引用的目标;

        直接引用(Direct Reference):指向内存中的实际地址的引用。java代码再运行时,需要将符号引用(类、方法、字段等)解析为直接引用,以便操作内存中的实际对象。

解析步骤:

  1. 定位目标:首先,JVM会根据符号引用的信息定位到目标对象,比如类、方法、字段等。这个定位过程可以通过查找类加载器的加载信息、运行时常量池等进行。

  2. 解析符号引用:一旦目标对象被定位,JVM会对符号引用进行解析。解析的过程就是将符号引用转化为直接引用的过程。在解析时,JVM会验证符号引用的正确性,并尝试将其转化为直接引用。

  3. 创建直接引用:一旦符号引用被解析为直接引用,JVM会根据直接引用创建相应的对象,比如类的实例、方法的直接引用等。

通过解析过程,JVM可以将在编译阶段生成的符号引用转化为运行时需要的直接引用,从而实现对类、方法、字段等的访问和操作。

总结起来,解析是将符号引用转化为直接引用的过程,让JVM能够准确地定位并操作内存中的实际对象。这个过程是类加载子系统中的重要环节,保证了Java程序的正确运行。

初始化:将类中的static属性和其他成员变量赋值操作(当然这些属性,有给默认值才执行赋值操作)。

 1.1.2 类加载分类

JVM 加载class文件是通过类加载器去加载的。 

类加载器分为两大类,引导类加载器(BootstrapClassLoader)自定义类加载器

引导类加载器(BootstrapClassLoader):非java代码写的代码,JVM 默认的类加载器,加载jre 下的 lib下面的jar包;

自定义类加载器:是使用java代码写的类加载器,继承ClassLoader。jvm中默认的自定义类加载器,扩展类加载器(ExtClassLoader)、应用类加载器(AppClassLoader)。ExtClassLoader加载的是 jre下的lib下的ext下面的 jar 包,AppClassLoader加载的是当前应用classpath下的类。

 1.1.3 双亲委派

作用:

  • 避免类的重复加载
  • 防止核心API被篡改

1.2 运行时数据区

1.2.1 程序计数器 

PC Register, 程序计数寄出去,简称程序计数器

(1)物理寄存器的抽象实现;

(2)记录待执行的下一条指令的地址;

(3)是程序控制流指示器,循环、if else、异常处理、线程恢复等都依赖于程序计数器实现;

(4)解释器工作时就是通过它来获取下一条需要执行的字节码的指令;

(5)程序计数器是JVM中唯一一个没有规定任何 OutOfMemoryError 情况的区域。

1.2.2 虚拟机栈(java方法栈)
1.2.2.1 虚拟机机构

每个线程再执行的时候,都会生成一个虚拟机栈,虚拟机栈保存一个个栈帧,每个栈帧对应一个方法。

每执行一个方法,都创建一个栈帧1,如果这个方法调用了另一个方法,那么会在虚拟机栈创建一个栈帧2,并且放入到虚拟机栈中,同理,再执行方法3和方法4都会生成栈帧3和栈帧4,并且存入虚拟机栈中。如果方法4执行完成,那么对应的栈帧就会出栈。依次执行方法3,2,1 都执行完成后,虚拟机栈就会消失。

虚拟机栈特点:

1. 虚拟机栈是线程私有的;

2. 每个方法执行都会创建一个栈帧存入虚拟机栈,方法执行完成,栈帧出栈。因此,虚拟机栈不需要垃圾回收;

3. 虚拟机栈存在OutOfMemoryError和StackOverflowError问题;

4. 线程太多,就可能出现OutOfMemoryError,线程创建时,没有足够内存去创建虚拟机栈了;

5. 方法调用层数太多,可能会出现StackOverflowError错误,虚拟机栈内存不足;

6. 可以通过 -Xss 来设置虚拟机栈的大小。

1.2.2.2 栈帧结构

每个栈帧由局部变量表、操作数栈、返回值地址、动态链接、附加信息组成。

1.2.2.2.1 局部变量表

 局部变量表有一个个Slot组成,一个栈帧相当于一个方法,在这个方法中我们通常会定义一些局部变量,存放再栈帧中的局部变量表当中,那么这个局部变量就是Slot

1.2.2.2.2 操作数栈

 说明:

1. java代码:定义了变量a、b、c,其中a = 10,b = 20,c = a + b;

2. 字节码指令:java代码解析为字节码后执行的指令

3. 局部变量表:存放字节码中变量 a,b,c 的表

执行过程说明:

 上图用第一个指令做了一个示例,下面将详细介绍执行流程

1.执行第一行字节码指令(bipush)将10压入到操作数栈中;

2.执行第二行字节码指令(istore_1)将10从操作数栈中拿出,存入到局部变量表索引为1的位置;

3.执行第三行字节码指令(bipush)将20压入到操作数栈中;

4.执行第四行字节码指令(istore_2)将10从操作数栈中拿出,存入到局部变量表索引为 2 的位置;

5.执行iload_1指令,将局部变量表下标为1的数据读出来,压入操作数栈;

6.执行iload_2指令,将局部变量表下标为2的数据读出来,压入操作数栈;

7.执行idd ,将操作数栈中的10 和20 进行加法操作,得到结果30;

8.执行istore_3,将30 从操作数栈中取出,存入局部变量表索引为3的位置;

9.执行返回return 指令,将局部变量表中的30 返回,并栈帧消失。

1.2.2.2.3 返回值地址

每个方法执行时都会创建一个栈帧,当方法结束后,将继续往下执行,那么这个栈帧中的返回值地址就是要执行下一条指令的地址。

1.2.2.2.4 动态链接

动态链接的主要目的是支持方法之间的调用和返回。为了实现动态链接,每个栈帧都包含了一个指向其调用者栈帧的引用。这个引用通常被称为"帧指针"或"前一帧指针"。通过帧指针,可以在运行时建立和维护方法调用链。当一个方法执行完成后,JVM会将其栈帧出栈,恢复到调用者方法的栈帧,并继续执行调用者方法的剩余部分。这种递归的栈帧结构和动态链接机制使得Java能够支持方法的嵌套调用和返回。

总结一下,动态链接是通过在栈帧中维护帧指针来实现的,它用于建立和维护方法之间的调用关系,支持方法的嵌套调用和返回。

1.2.3 本地方法栈

本地方法:navive method,在java中定义的方法,但是是由其他语言实现;

本地方法栈存放的是本地方法调用的栈帧;

线程私有,也可能出现OOM(OutOfMemeryError)和SOF(StackOverflowError)。

1.2.4 堆

JVM 运行时最重要的一块区域。JVM 规范中所有的对象和数组都存放在该区域,在执行字节码指令时,会将创建的对象放入堆中,对象引用的地址放入虚拟机栈的栈帧中,当方法执行完成后,引用的对象不会立即被回收,而是需要等待守护线程GC后,才能被回收。

-Xms:ms(memory start),指定堆初始化内存大小,等价于:-XX:InitialHeapSize

-Xmx:mx(memory max),指定堆最大内存大小,等价于:-XX:MaxHeapSize

一半会把 -Xms和 -Xmx 设置为一样大小,原因是JVM在GC后不需要去调整内存大小,提高效率。

默认情况下,初始化内存大小=物理内存大小 / 64,最大内存大小=物理内存 / 4

可以通过 -XX:NewRatio 参数来设置新生代和老年代的比例,默认是2,表示新生代占1,老年代占2,也就是新生代内存占堆区总大小的 1/3; 一半是不需要调整的,只有明确知道存活时间比较长的对象偏多,就需要调大 NewRatio来调整老年代占比。

Eden:伊甸园区,新的对象会存放到Eden区(除非对象大小超过了Eden区,那就只能直接进入老老年代);S0(Servivor0)、S1(Servivor1)区,也可以叫from区和to区,用来存放MinorGC(YGC)后存活的对象

默认情况下(Eden : S0 : S1 内存比例为 8 : 1 : 1), 可以通过 -XX:ServivorRatio 来调整

Yong GC / Minor GC(YGC):负责对新生代进行垃圾回收;

Old GC / Major GC:负责对老年代进行垃圾回收,目前只有CMS垃圾回收器单独对老年代进行GC,其他垃圾回收器是对整堆进行GC,同时对老年代进行GC;

Full GC:整堆回收,包括方法区。

创建新对象和回收过程详细说明:

  1.  图1中,创建多个对象,有写对象在执行完成后不在引用(灰色)即垃圾对象,有的继续引用(粉色),当Eden区域满了之后会进行一次MinorGC(YGC),存活的对象会进入到S0区,并且给存活的对象标记上数字1,表示经历过一次MinorGC,如图2所示;

2. 再次当Eden区对象满了之后,再次进行MinorGC,会将垃圾对象清理,如下图3。将Eden存活的对象放入S1区,并且给对象标记上数字1,表示经历过一次MinorGC;将S0区存活的对象也放入到S1区,并且给对象标记上数字2,表示又经历过一次MinorGC,如下图4所示。 

 3. 继续创建新对象,并放入Eden区,当Eden区再次满了,会再次进行MinorGC,会将垃圾对象清理,如下图5。将Eden存活的对象放入S0区,并且给对象标记上数字1,表示经历过一次MinorGC;将S0区存活的对象也放入到S0区,并且给对象标记上数字3,表示又经历过一次MinorGC,如下图6所示。

 4. 不断重复这些过程,有些对象一直存活,经历过15次MinorGC,如下图7所示;最后一直存活的对象,将会存放再老年代中,如下图8所示

 5、如果Eden有个大对象,导致Eden直接满了,执行MinorGC,由于S0和S1区内存不足于存放该大对象,则直接会放到老年代中,如图9、图10所示;

 6.如果堆中创建了一个超大对象,Eden区、S0区、S1区都放不下,则直接会放入到老年代,如图11、图12所示;

1.2.5 方法区

方法区和堆一样,是一个被其他线程共享的区域。主要存储解析器解析后的机器指令、类信息、方法信息、常量池、静态变量等。

JDK 1.8之前,称之为永久代,实际上在1.7版本的时候已经有一部分不是永久代了,比如常量池,静态变量已经移出永久代,到了1.8则完全使用元空间(本地内存)替代。

`

1.3 垃圾回收

1.3.1 为什么要有垃圾回收

在JVM中,没有任何引用指向的对象称之为垃圾。垃圾不清理,就会一直占用着内存,内存中的对象越来越多,最终会导致OOM。

1.3.2 垃圾回收算法
1.3.2.1 引用计数法

每个对象都记录一个引用计数器属性,用来记录被引用的次数。

优点:实现简单,只要引用计数器为0,就是垃圾

缺点:

  • 无法解决循环引用问题
  • 需要额外空间存储
  • 需要额外时间维护

1.3.2.2 可达性分析

可达性分析是一GC root 为起点,一层一层地找到所引用的对象,被找到的就是存活的对象,其他不可达的对象称为垃圾对象。 

GC Roots 是一组引用,包括:

  • 虚拟机栈中正在执行的方法对应的参数,局部变量表中对象的引用;
  • 本地方法栈中正在执行的方法对应的参数,局部变量表中对象的引用;
  • 方法区中保存的类中的static属性所对应的对象引;
  • 方法区中保存的类中的常量属性所对应的对象引用等等;
1.3.3 回收算法
1.3.3.1 标记-清除

 

常用的垃圾回收算法,当新生代或者老年代内存不足,则会暂停所有用户线程,即STW(Stop The World),执行算法进行垃圾回收。

A.标记阶段:从GC roots 开始遍历,找到可达对象,并在对象头中进行记录;

B.清除阶段:堆内存空间进行线性遍历,如果发现对象头中没有记录此对象是可达的对象,则进行回收。

标记-清除算法优缺点:

优:思路简单

 缺:

        A.效率不高;

        B.内存碎片;由图可以看出来,内存是分散的,不是连续的,会导致有些对象没法存入。

1.3.3.2 复制

将内存分为A、B两块,每次只使用一块,在垃圾回收的时候,将可达对象存入到另一块空的内存中,然后再清除当前内存块中不可达对象。

复制算法优缺点:

优:

1.没有标记-清除阶段,直接将可达对象复制,不需要修改对象头,效率高

2.不会出现内存碎片

缺: 

1.需要更多的内存,总有一块内存没有被使用

2.对象复制后,内存地址发生改变,需要修改栈帧中对象引用的地址

3.如果可达对象比较多,那么复制效率就很低,所以适合垃圾对象多的情况(通常用在新生代)

1.3.3.3 标记-整理

 

分为三个阶段:

1. 第一阶段,从GC Roots开始遍历,找到可达对象,并在对象头中记录;

2. 第二阶段,将可达对象移动到内存的另一端;

3. 第三阶段,清理边界外所有空间。

优缺点:

优:

1. 不会产生内存碎片

2. 不需要额外的内存空间

缺:

1.效率低下

2.需要修改栈帧中的内存地址

三个算法比较,如下图:

 1.3.4 常见垃圾收集器

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
你可以通过以下步骤在 Linux 虚拟机上进行深度学习开发: 1. 选择虚拟化软件:首先,你需要选择一个虚拟化软件,如 VirtualBox、VMware 或 KVM 等。这些软件可以帮助你创建和管理虚拟机。 2. 下载和安装虚拟化软件:根据你的操作系统,下载和安装适当的虚拟化软件。在安装过程中,请按照软件提供的指示进行操作。 3. 下载 Linux 镜像:在虚拟化软件的官方网站上下载适合你需要的 Linux 发行版的镜像文件。你可以选择 Ubuntu、CentOS、Fedora 等。 4. 创建虚拟机:打开虚拟化软件并创建一个新的虚拟机。在创建过程中,请指定虚拟机的名称、内存大小、硬盘空间和网络设置等。 5. 安装 Linux 操作系统:在虚拟机中启动,然后使用之前下载的 Linux 镜像文件进行安装。按照安装向导的指示完成操作系统的安装。 6. 更新操作系统和安装必要软件:在完成安装后,更新操作系统并安装必要的软件包,例如开发工具链、Python、深度学习框架(如 TensorFlow、PyTorch)等。 7. 配置 GPU 支持(如果有):如果你的物理机器具有 GPU,你可以在虚拟机中启用 GPU 支持。这通常涉及安装 GPU 驱动程序和 CUDA 工具包。 8. 开始深度学习开发:现在你已经准备好在 Linux 虚拟机上进行深度学习开发了。你可以使用 Jupyter Notebook、PyCharm 等工具来编写和运行深度学习代码。 请注意,深度学习对计算资源要求较高,特别是在处理大型数据集或模型时。确保你的物理计算机或虚拟机具有足够的内存、存储和计算能力来满足你的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值