简述JVM

文章目录

  • JVM简介
  • JVM运行时数据区
    • 堆(线程共享)
    • 方法区/元空间/元数据区(线程共享)
    • 程序计数器
  • JVM类加载
    • 类加载过程
    • 双亲委派模型
  • 垃圾回收机制(GC)
    • 判断对象是否为垃圾
      • 判断是否被引用指向
    • 如何清理垃圾, 释放对象?

JVM简介

JVM 是 Java Virtual Machine 的简称, 意为Java虚拟机.

虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。

JVM运行时数据区

也就是JVM的内存布局

在这里插入图片描述

堆(线程共享)

保存程序中创建的对象

方法区/元空间/元数据区(线程共享)

存放被JVM加载的类信息(类对象), 常量, 静态变量(static), 即时编译器编译后的代码等数据

存放方法的调用关系, 局部变量

程序计数器

记录了当前线程执行的下一条指令的内存地址


class Test {public int n = 20;public static int a = 10;
}
public class Main() {public static void main(String[] arg) {Test t = new Test();}
}

n是普通的成员变量, 就包含在new的对象的内部, 存放在堆上

a是一个静态成员变量, 包含在类对象中, 存放在方法区中

t是一个局部变量, 存放在栈上

new Test()这个对象是保存在堆上的

栈上的t保存了堆上的new Test()的内存地址

JVM类加载

Java程序一开始是一个.java文件, 通过javac编译成.class文件, 运行java程序, JVM就会读取.class文件, 把文件的内容加载到内存中, 并构造成一个.class对象

类加载就是: 把类从硬盘文件加载到内存中.

类加载过程

流程:

  1. 加载: 找到.class文件, 打开文件, 并读取文件内容, 并且尝试解析格式

  2. 验证: 检查当前.class文件是否符合标准格式

  3. 准备: 给类对象分配内存. 分配出来的内存空间, 内容就是全0的值.

  4. 解析: 将常量池内的符号引用替换为直接引用的过程, 也就是初始化常量的过程. 初始化类对象中涉及到的一些字符串常量, 这些字符串常量在.class文件中已经存在, 直接读到内存中就行.

    • 符号引用: 偏移量. 在.class文件中不知道字符串真实的内存地址在哪, 只知道一个相对的偏移量, 知道字符串的内容在.class文件的哪个地方.
    • 直接引用: 真实的内存地址
  5. 初始化: 对类对象进行更具体的初始化操作. 初始化金泰城园, 执行静态代码块, 加载父类…


双亲委派模型

实则单亲.

JVM加载.class文件的时候, 需要用到"类加载器"模块.

JVM中自带了三个类加载器:

  • Bootstrap ClassLoader: 负责加载标准库中的类
  • Extension ClassLoader(父亲是Bootstrap ClassLoader): 负责加载JVM扩展的库
  • Application ClassLoader(父亲是Extension ClassLoader): 负责加载第三方库.

不是父类子类的继承关系, 而是对象里有一个parent引用指向 父 类加载器 实例

当接收到类加载请求时:

  • Application ClassLoader是类加载的入口, 不会立即就搜索第三方库的目录, 而是先把任务委派给父亲, 让父亲先尝试加载.
  • 到了Extension ClassLoader, 也不会立即搜索扩展库的目录, 把任务委派给父亲, 让父亲先尝试加载.
  • 到了Bootstrap ClassLoader, 也不会立即搜索扩展库的目录, 把任务委派给父亲, 但是他没有父亲, 就只能自己来搜索了. 如果找到了这个类, 就会进行后续的加载流程; 如果没有, 那么任务就会交付给孩子来解决.
  • 如果任务回到了Extension ClassLoader, 他就要搜索扩展库的目录, 如果找到了这个类, 就会进行后续的加载流程; 如果没有, 那么任务就会交付给孩子来解决.
  • 如果任务回到了Application ClassLoader, 他就要搜索第三方库的目录, 如果找到了这个类, 就会进行后续的加载流程; 如果没有, 那么就抛出异常.

这样做的目的: 明确类的优先级(标准库的类最优先加载, 扩展库其次, 第三方库最低)

标准库中有一个类java.lang.String, 如果我们自己也写了一个java.lang.String类

JVM始终是加载标准库里的类, 而不会加载到我们写的类, 这样便可以避免程序员的代码对标准库的代码产生负面影响


类加载的时机?(类似于懒汉模式, 用到了才加载)

  1. 构造了这个类的实例
  2. 使用了该类的静态方法, 静态属性
  3. 子类的加载会触发父类加载.

垃圾回收机制(GC)

对于程序计数器, 栈而言, 它的生命周期与相关的线程有关, 随线程生, 随线程灭, 并且这两个区域的内存分配和回收具有确定性, 因为当方法或者线程结束了, 内存就自然跟着线程回收了. 所以垃圾回收主要是回收堆和方法区这两个区域.

缺点: 消耗额外的系统资源, 消耗一定的时间, 可能有STW问题

垃圾回收是以对象为单位进行回收

垃圾回收的流程分为两步: 1. 判定对象是否为垃圾. 2. 释放对象的内存

判断对象是否为垃圾

一个对象, 如果在后续代码中不会被继续使用到了, 就可以视作是垃圾了

  • 不会被继续使用: 没有被任何引用指向

    public void test() {T t = new T();t.func();
    }
    test();
    

    调用方法test的时候, 局部变量t被创建, 当test方法执行完了之后, t自然销毁, 此时new T()就没有被引用指向了, 这个对象也就是垃圾了.

判断是否被引用指向

  1. 引用计数

    给这个对象里面安排一个计数器, 每次有引用指向它, 就把计数器+1, 每次引用被销毁, 计数器-1, 当计数器为0, 意味着该对象就是垃圾了

    class Test {//
    }
    Test t = new Test();//new Test()里的计数器为1
    Test t2 = t;//new Test()里的计数器为2
    Test t3 = t2;//new Test()里的计数器为3
    t = null;//new Test()里的计数器为2
    t2 = null;//new Test()里的计数器为1
    t3 = null;//new Test()里的计数器为0, 此时该对象为垃圾了
    

    该方法并非是JVM中使用的方案, Python和PHP的虚拟机GC采用的是此方案

    缺陷:

    • 空间利用率低, 浪费更多的内存空间

    • 可能存在循环引用的问题, 导致对象不能正确识别为垃圾.

      class Test {public Test t;
      }
      Test a = new Test();//计数器为1
      Test b = new Test();//计数器为1
      a.t = b;//计数器为2
      b.t = a;//计数器为2
      a = null;//计数器为1
      b = null;//计数器为1
      

      在这里插入图片描述

      此时这两个对象的引用计数不为0, 不能被当做垃圾. 与此同时, 一想要使用对象1, 就需要访问对象2. 要想使用对象2, 就得先访问到对象1…这就是循环引用

  2. 可达性分析(JVM使用的方案)

    JVM首先会从现有代码中的能直接访问到的对象出发, 尝试便利所有能访问的对象, 只要对象能访问到, 就会标记成"可达", 完成整个遍历之后, 不可达的对象, 就相当于是垃圾了.

    这样的操作没有额外的空间开销, 但是消耗了更多的时间.

    那些是能直接访问到的对象呢?

    这些对象又被称为gc roots.

    1. 栈上的局部变量.
    2. 常量池里的引用
    3. 方法区中类静态属性引用的对象
    4. 本地方法栈中 JNI(Native方法)引用的对象

    代码执行过程中, 一个对象是否是垃圾, 往往是动态变化的(之前不是垃圾, 现在是垃圾了), 所以可达性分析的扫描是持续的周期性的

如何清理垃圾, 释放对象?

  1. 标记清除: 被标记为垃圾的对象直接清除.

    弊端: 申请内存的时候, 都是申请的连续的空间, 直接释放会导致内存的碎片化, 会破环原有的连续性, 可能会导致有内存, 但是申请不了.

  2. 复制算法: 通过冗余的内存空间, 把有效的对象复制到另一部分空间, 避免内存碎片.

    把一个内存分成两份, 一份使用, 一份等待有效对象被复制过来.

    弊端: 如果复制的对象多, 开销会很大, 而且内存利用率也不高, 相当于浪费了一般的内存.

  3. 标记整理: 类似于顺序表的删除元素操作.

    弊端: 这样的方式虽然解决了复制算法内存利用率低的问题, 但是搬运对象的成本也比较高.

  4. 分代回收: 采用分治的思想, 进行代的划分, 把不同生命周期的对象放在不同代上, 不同代上采用最适合它的垃圾回收方式进行回收.

    Java的对象大体分为两类: 1. 生命周期很长的; 2.生命周期很短的

    不同的对象的生命周期是不一样的. 因此, 不同生命周期的对象可以采取不同的收集方式, 以便提高回收效率.

    如何分代?

    JVM根据对象存活的周期(GC周期性扫描)不同, 把对内存划分了2块, 为新生代老年代.


    新生代又分为伊甸区(Eden)和幸存区(Survivor).

    伊甸区占大部分内存, 这里存储的对象生命周期都很短. 经过一次GC后, 存活下来的对象就会通过复制算法复制到幸存区.

    幸存区是两块大小相等的内存区域, 每次只用一块, 如果这里的对象经过GC后存货, 会继续被复制到另一块幸存区, 如此往复.如果一个对象在幸存区里经过了很多轮GC还存活, 证明它的生命周期很长, 那么它就会被复制到老年代.


    老年代的GC频率比新生代要低. 这里清理垃圾的策略是标记整理

    如果一个对象的体积很大, 那么他会直接进入老年代, 因为这样的对象不适合进行复制

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

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

相关文章

加速计算卡设计方案:389-基于KU5P的双路100G光纤网络加速计算卡

基于KU5P的双路100G光纤网络加速计算卡 一、板卡概述 基于Xilinx UltraScale16 nm KU5P芯片方案基础上研发的一款双口100 G FPGA光纤以太网PCI-Express v3.0 x8智能加速计算卡,该智能卡拥有高吞吐量、低延时的网络处理能力以及辅助CPU进行网络功能卸载的能力…

Spring Security 6.1.x 系列(2)—— 基于过滤器的基础原理及源码解析(一)

一、过滤器 Spring Security 的 Servlet 支持基于 Servlet 过滤器,因此首先了解过滤器的作用会很有帮助。 下图为单个 HTTP 请求的处理程序的典型分层。 客户端向应用程序发送一个请求,运行容器创建一个FilterChain(过滤链)&…

Realtek 5G pcie网卡 RTL8126-CG简介

总shu:PCIE 5G网卡方案“RTL8126-CG”采用QFN56封装,面积8 x 8毫米,非常小巧,提供一个RJ-45网口、两个USB 3.x接口。它走的是PCIe 3.0 x1系统通道,搭配超五类网线,可以在长达100米的距离上提供满血的5Gbps网…

股权比例设计的九条生命线

股权比例设计——绝对控制线67% 【释义】一些重大事项如公司的股本变化,关于公司的增减资,修改公司章程, 分立/合并、变更主营项目等重大决策,需要2/3以上(含2/3)票数支持的。 股权比例设计——相对控制线…

Apollo上车实践:打造安全、高效、舒适的出行体验

上车实践 概述自动驾驶车辆适配线控标准协议开环验证车辆 自动驾驶车辆集成了解传感器布置与连接了解车辆标定了解传感器标定循迹实践 自动驾驶测试与调车了解车辆安全操作流程了解实车控制调试了解实车定位调试 福利活动 主页传送门:📀 传送 概述 通过…

Linux--安装与配置虚拟机及虚拟机服务器坏境配置与连接---超详细教学

一,操作系统介绍 1.1.什么是操作系统 操作系统(Operating System,简称OS)是一种系统软件,它是计算机硬件和应用软件之间的桥梁。它管理计算机的硬件和软件资源,为应用程序提供接口和服务,并协调…

【配置环境】VS Code中JavaScript环境搭建

一,环境 Windows 11 家庭中文版,64 位操作系统, 基于 x64 的处理器VS Code 版本: 1.83.1 (user setup)Node.js 版本:20.9.0 二,为什么搭建JavaScript环境 因为在看《重构改善既有代码的设计第2版》的时候,书中的代码展…

概念解析 | 雷达动态范围

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:雷达动态范围。 揭开雷达动态范围的神秘面纱 雷达动态范围是描述雷达系统性能的一个重要指标,它直接影响着雷达的检测能力。但是这个指标又比较抽象,对于非专业人员来说不太容易…

Apache ActiveMQ (版本 < 5.18.3) (CNVD-2023-69477)RCE修复方案/缓解方案

一、漏洞描述 Apache ActiveMQ 是美国阿帕奇(Apache)基金会的一套开源的消息中间件,它支持 Java 消息服务、集群、Spring Framework 等。 二、漏洞成因 ActiveMQ 默认开放了 61616 端口用于接收 OpenWire 协议消息,由于针对异常…

图纸管理制度《七》

1、目的: 明确技术图样与文件的签署、更改及标准化等内容,对技术图样与文件进行有效的控制。技术文化是公司的核心秘密,是公司能够持续发展并在市场上保持强势竞争力的有力保障,公司的技术文件属于公司所有,公司的每一…

当在本地,向服务器发送信息时,服务器接受信息返回给客户端,此时采用多行读取时,客户端接收不到Server的信息

public class SocketTCP04Server {public static void main(String[] args) throws IOException {ServerSocket serverSocket new ServerSocket(9999);System.out.println("Server:"serverSocket.getClass());System.out.println("正在等待用户连接.…

1.8 网络安全模型

思维导图: 1.8 网络安全模型笔记: 网络安全模型核心概念: 消息在Internet上从发送方传送至接收方,涉及到源地址、目的地址、通信协议(如TCP/IP)的使用。信息交换的双方需要合作保证交换的可靠性。 安全技术核心组成…