首页 > 其他分享 >JVM

JVM

时间:2023-08-17 19:55:08浏览次数:31  
标签:Eden Space 对象 GC 内存 JVM 加载

JVM

1. JVM的位置

  1. 运行在操作系统上
    image

2. jvm的体系结构

image

3. 类加载器

  1. 作用:加载class文件

  2. 虚拟机自带的加载器

    启动类(根)加载器:JAVA_HOME\lib\jar包 or rt.jar

    扩展类加载器:JAVA_HOME\lib\ext

    应用程序(用户)加载器:classpath

4. 双亲委派机制

为了保证安全:自定义-->app->ext->bootstrap

使用不同的类加载器最终得到的都是同样一个 Object 对象

  1. 类加载器收到类加载请求
  2. 将请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
  3. 启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前类的加载器,否则抛出异常,通知子类加载器进行加载
    image

5. 沙箱安全机制

  1. java安全模型的核心是java沙箱
  2. 主要限制系统资源(CPU、内存、文件系统、网络)的访问。
  3. 不同级别的沙箱对系统资源访问的限制也有差异。

6. Native

凡是带了native关键字的,说明java的作用范围达不到了,回去调用底层C语音的库

7. PC寄存器

  1. 每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存,就是一个指针,指向方法区的方法字节码。
  2. 程序计数器是每个线程私有的。存储的数据所占空间的大小不会随程序的执行而发生改变,所以不会发生内存溢出

8. 方法区

  1. 类信息(构造方法、接口定义)、常量、静态变量、即时编译器编译后的代码

static/final/Class/常量池

  1. 如果线程正在执行得是一个Java方法,计数器记录的是正在执行得虚拟机字节码指令的地址
  2. 如果正在执行的是Nativa方法,这个计数器值则为空
  3. jdk7叫作永久代,jdk8叫作元空间

9. 栈

  1. 数据结构

  2. 程序 = 数据结构 + 算法

  3. 栈:先进后出,后进先出

    队列:先进先出(FIFO:first input first output)

  4. 每个方法在执行的同时都会创建一个栈帧

  5. 主管程序的运行,生命周期与线程同步,一旦线程结束,栈就OVER

  6. 存放基本数据类型、对象引用
    image

10. 堆

  1. 一个JVM只有一个堆内存,堆内存大小可以调节
  2. 类、方法、常量、变量、引用类型的真实对象
  3. 所有的对象实例以及数组都要在堆上分配

10.1 细分

image

10.1.1 新生代

  1. 一般占据堆的1/3空间
  2. 由于频繁创建对象,新生代会频繁触发MinorGC进行垃圾回收
  3. 新生代又分为:Eden ServivorFrom ServivorTo

10.1.1.1 Eden

  • 新建对象的出生地
  • 如果新创建的对象占用内存很大,则直接分配到老年代)
  • 当 Eden 区内存不够的时候就会触发 MinorGC,对新生代区进行一次垃圾回收

10.1.1.2 ServivorFrom

  • 上一次 GC 的幸存者,作为这一次 GC 的被扫描者

10.1.1.3 ServivorTo

  • 保留了一次 MinorGC 过程中的幸存者

10.1.2 MinorGC

  1. 复制

​ eden、servicorFrom复制到 ServicorTo,年龄+1

  • 首先,把 Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域(如果有对象的年
    龄以及达到了老年的标准,则赋值到老年代区)

  • 同时把这些对象的年龄+1(如果 ServicorTo 不够位置了就放到老年区)

  1. 清空

​ 清空eden、servicorFrom

  • 清空 Eden 和 ServicorFrom 中的对象
  1. 互换

​ ServicorTo和 ServicorFrom互换

  • ServicorTo 和 ServicorFrom 互换,原 ServicorTo 成为下一次 GC 时的 ServicorFrom

10.1.3 老年代

  1. 存放应用程序中生命周期长的内存对象
  2. 老年代的对象比较稳定,所以 MajorGC 不会频繁执行
  3. 在进行 MajorGC 前一般都先进行了一次 MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发
  4. 当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次 MajorGC 进行垃圾回收腾出空间

10.1.3 MajorGC

  1. 扫描一次所有老年代,标记出存活的对象
  2. 回收没有标记的对象
  3. MajorGC 的耗时比较长,因为要扫描再回收
  4. MajorGC 会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。
  5. 当老年代也满了装不下的时候,就会抛出 OOM异常

10.1.4 永久代

  1. 存放jdk自身携带的class对象。接口元数据,存储的是Java运行时的一些环境或者类信息

  2. Class 在被加载的时候被放入永久区域,它和和存放实例的区域不同

  3. GC 不会在主程序运行期对永久区域进行清理,导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常

  4. jak1.6之前:永久代,常量池在方法区

    jdk1.7:永久代,但慢慢退化了,常量池在堆中

    jdk1.8之后:无永久代,常量池在元空间

11. 常见异常

  1. OutOfMemoryFrror:内存溢出
  2. StackOverflowError:栈溢出

12. 类加载过程

12.1 加载

把class文件加载到内存中

  1. 根据类名,找到class文件
  2. 把class文件加载到内存
  3. 创建一个类对象

12.2 验证

确保class文件的字节流包含的消息是否符合JVM标准,是否危害到虚拟机自身的安全

12.3 准备

给静态变量赋0值

12.4 解析

初始化常量(虚拟机将常量池的符号引用替换为直接引用的过程)

12.5 初始化

执行构造方法

13. 垃圾回收

13.1 判断对象是死亡对象

13.1.1 引用计数法

  1. 给对象加一个引用计数器
  2. 每当有一个地方引用它,计数器+1
  3. 引用失效,计数器-1
  4. 当计数器为0时,为死亡对象

优点:

  1. 简单

缺点:

  1. 会浪费一部分空间用来计数,加入一个空间对象是2个字节,计数的空间为4个字节
  2. 不能解决循环引用问题

13.1.2 可达性分析法

  1. 通过一系列的“GC roots”对象作为起点搜索
  2. 如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的
  3. 不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程
  4. 两次标记后仍然是可回收对象,则将面临回收

13.2 垃圾回收法

13.2.1 复制算法

  1. 将内存分为容量大小相等的两个,分别是From区和To区,新创建的对象都放在From区
  2. 当From区的内存不够存储下一个对象的时候,就将From区中存活的对象复制到To区,然后再将From区一次性全部清空回收,此时From区和To区的职责发生变化,原来的From区变为To区,原来的To区变为From区

优点:

  1. 有效解决内存碎片的问题

缺点:

  1. 当对象的存活率较高的时候使用这种算法的效率较低
  2. 内存空间资源的浪费

13.2.2 标记清除法

标记出所有需要回收的对象,回收被标记的对象所占用的空间

优点:

  1. 算法思路简单清晰

缺点:

  1. 效率低下
  2. 需要维护空间碎片

13.2.3 标记压缩法

标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象

优点:

  1. 避免了内存碎片的产生

缺点:

  1. 对于内存中存活对象较多的时候,使用这种算法需要做大量的移动工作,效率性能低下

13.2.4 分代法

根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将 GC 堆划分为新生代和老年代,持久代。

1. 新生代

  1. 新生代的特点是每次垃圾回收时都有大量垃圾需要被回收。

  2. 大部分新生的对象都放在新生代中,该代中对象的存活时间较短,GC频次较高,使用复制算。

  3. 新生代分为3个区:一个Eden区和两个Survivor区。

  4. 当Eden区满了之后,将存活的对象复制到任意一个Survivor区,当其中一个Servicor区满了之后,将这个区中存活的对象复制到另外一个Survivor区中,当这个Survivor区也满了之后,从Survivor区A中复制过来的对象如果还存活着就将他复制到老年代中(需要注意的是Survivor区既存在从Eden区过来的对象,也存在从另外一个Survivor区复制过来的对象,总有一个Survivor区是空的,Survivor区是可以配置多个的(多于2个),这样做的目的是为了让对象尽可能的存在于新生代,也就是延长对象在新生代的存在时间)

2. 持久代

  1. JAVA 虚拟机提到过的处于方法区的永久代(Permanet Generation),它用来存储 class 类,常量,方法描述等
  2. 对永久代的回收主要包括废弃常量和无用的类。

3. 老年代

  1. 对象的内存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目
    前存放对象的那一块),少数情况会直接分配到老年代。

  2. 当新生代的 Eden Space 和 From Space 空间不足时就会发生一次 GC,进行 GC 后,Eden
    Space 和 From Space 区的存活对象会被挪到 To Space,然后将 Eden Space 和 From
    Space 进行清理。

  3. 如果 To Space 无法足够存储某个对象,则将这个对象存储到老年代。

  4. 在进行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反复循环。

  5. 当对象在 Survivor 区躲过一次 GC 后,其年龄就会+1。默认情况下年龄到达 15 的对象会被
    移到老年代中。

13.2.5 总结

内存整齐度:复制算法=标记压缩算法>标记清除算法

内存效率(时间复杂度):复制算法>标记清除算法>标记压缩算法

内存利用率:标记压缩算法=标记清除算法>复制算法

标签:Eden,Space,对象,GC,内存,JVM,加载
From: https://www.cnblogs.com/yqquinn/p/17638719.html

相关文章

  • 形象谈JVM-第三章-即时编译器优化技术
    即时编译器优化技术一览:相信许多同学看完这个表格,脑子里面嗡嗡的,这些名字也是晦涩难懂,要实现这些优化的技术确实有比较大的难度,但是咱们只是学习,去理解这些技术,其实并不难,下面咱们直接开讲。首先需要明确一点的,作者是为了讲解方便,使用java的语法来表示优化技术所发挥出来的作......
  • 一文了解JVM垃圾回收机制和常用算法
    垃圾收集(GarbageCollection,GC)垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。判断一个对象是否可被回收如果一个或多个对象没有任何的引用指......
  • JVM调优(十七)JVM常见调优问题和工具的使用
    JVM调优(十七)JVM常见调优问题和工具的使用说辞熟悉GC常见算法熟悉常见的垃圾回收器,具有实际JVM调优经验1什么是调优根据需求进行JVM优化和预调优优化JVM的运行环境(慢、卡顿)解决JVM运行过程中出现的各种问题(OOM)2JVM常用调优命令jps:JDK自带,全称javaprocess,列出系......
  • JVM相关面试题
    JVM的组成程序计数器Java堆虚拟机栈其实就是线程运行时需要的内存......
  • JVM相关
    对象头在JVM中,对象在内存中的布局分为3块:对象头、实例数据和对齐填充。实例数据:程序代码中定义的各种类型的字段内容。对齐填充:JVM要求对象的大小必须是8个字节的整数倍,对象头已经是8的整数倍了,如果实例数据没有8的整数倍就需要对齐填充来补全。对象头=MarkWord+类型......
  • 形象谈JVM-第二章-认识编译器
    我在上一章《形象谈JVM-第一章-认识JVM》提到的“翻译”,其实就是我们今天所说的“编译”的概念。上一章原文链接:https://www.cnblogs.com/xingxiangtan/p/17617654.html原文:【虚拟机的职责是将字节码翻译成对应系统能够识别并执行的机器码,比如在linux系统,java文件被javac编译......
  • Java | JDK、JRE、JVM的关系
    一、什么是JDK、JRE、JVM?JDK(JavaDevelopmentKitJava开发工具包)是提供给Java开发人员使用的,其中包含了java的开发工具集,也包括了java的运行环境JRE。它是开发者在进行Java应用程序开发时所需的完整套件。JRE(JavaRuntimeEnvironmentJava运行环境)包括java虚拟机和Java程......
  • JVM——内存分配与垃圾回收
    内存分配与垃圾回收1、jvm简介Java虚拟机在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域。它们各有用途,有些随着虚拟机进程的启动一直存在(堆、方法区),有些则随着用户线程的启动和结束而建立和销毁(程序计数器、虚拟机栈、本地方法栈)。JVM的设计者们之所......
  • JVM——类加载机制
    任何一个类型在使用之前都必须经历过完整的加载、连接和初始化3个类加载步骤。一旦一个类型成功经历过这3个步骤之后,它就可以随时随地被使用了,开发人员可以在程序中访问和调用它的静态类成员信息(比如:静态方法、静态字段),或者使用new关键字为其创建对象实例。当然从一个类型被加载进......
  • JVM之字节码的编译原理
    JVM之字节码的编译原理Java最初诞生的目的就是为了在不依赖特定的物理硬件和操作系统环境下运行,那么也就是说Java程序实现跨平台我的基石其实就是字节码。Java之所以能够解决程序的安全性问题、跨平台移植性等问题,最主要的原因就是Java源代码的编译结果并非是本地机器指令,而是字......