• jvm总结


    目录

    JVM架构图

    简要流程:

    类加载器

    类加载器原则

    三种默认的类加载器

    双亲委派机制(保证安全性)

    类的生命周期

    类的验证阶段内容

    类的直接引用与被动引用

    类的直接引用

    类的被动引用

    执行引擎

    主要组件

    JVM内存模型

    JVM内存结构

    垃圾回收机制与内存泄漏

    GC(garbage collection)的分类

    GC判断策略

    引用计数

    可达性分析(解决循环依赖问题)

    GC收集算法

    复制算法

    标记清除算法

    标记整理算法

    分代垃圾收集 

    JVM内存(堆里)

    关于新生代与老生代的理解 

    回收方法区

    如何判断无用的类

    JVM架构图

    简要流程:

    程序在执行之前先要把java代码转换成字节码文件(class文件),jvm首先需要把字节码通过一定的方式 类加载器(ClassLoader) 把文件加载到内存中 运行时数据区(Runtime Data Area) ,而字节码文件是jvm的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器 执行引擎(Execution Engine) 将字节码翻译成底层系统指令再交由CPU去执行,而这个过程中需要调用其他语言的接口 本地库接口(Native Interface)来实现整个程序的功能。

    注意:运行时数据区好比作是制作一道菜需要的食材、工具、调味料等,而执行引擎可以比作是一个厨师,厨师将准备好的东西制作成一道精品菜肴就好比是成功运行Java引用程序。

    类加载器

    含义:类加载器是一个用来加载类文件的类

    类加载器作用:在运行时加载类,其会将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区运行时数据结构,然后在堆中生成一个代表这个类的java.lang.class对象作为方法区中类数据的访问入口

    类加载器原则

    • 单一性原则:类加载器不能多次加载同一个类
    • 委托机制:将加载一个类的请求交给父加载器,如果这个父加载器不能找到或加载这个类,那么再加载他
    • 可见性:子类加载器可以看见所有父加载器加载的类,而父加载器看不见子类加载器加载的类

    三种默认的类加载器

    1. //系统类加载器(system类加载器)
    2. ClassLoader a = ClassLoader.getSystemClassLoader();
    3. //扩展类加载器(extension类加载器)
    4. ClassLoader b = a.getParent();
    5. //根加载器(bootstrap类加载器)
    6. ClassLoader c = b.getParent();

    注意:Bootstrap类加载器是由c来写的,java调用不到,其他的类加载器都是由java.lang.classloader来实现的

    双亲委派机制(保证安全性)

    1. 类加载器收到类的加载请求
    2. 将这个请求向上委托给父加载器去完成,一直向上委托,直到根加载器
    3. 根加载器检查是否能够加载当前这个类(就是检查是否有这个类),能加载则使用当前类加载器,否则通知子类加载器进行加载
    4. 重复步骤3

    类的生命周期

    加载阶段

    1. 加载类的信息到jvm方法区
    2. 在堆区中实例化一个java.lang.class类指向方法区中对应的类

    连接阶段

    • 验证阶段:验证类是否合法
    • 准备阶段:为类的静态变量分配内存,并设置初始值
    • 解析阶段:把常量池中的引用(标识符)转化为内存地址

    初始化阶段

    • 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并产生的(类构造器是构造类信息的,不是构造类对象的构造器)
    • 虚拟机会保证一个类的()方法在多线程的环境中被正确加锁和同步
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发父类初始化
    • 初始化工作包括由上而下依次执行类中的静态赋值语句和静态代码块(可能在解析之后)

    使用阶段

    使用类时只有直接引用的一个类才会将该类初始化,间接引用不会造成该类的初始化

    卸载阶段

    在jvm垃圾回收时,若检测到类不会再被使用时卸载该类

    类的验证阶段内容

    • 类文件的结构检查
    • 语义检查
    • 字节码检查
    • 二进制兼容性检查

    类的直接引用与被动引用

    类的直接引用

    • 设置或调用static变量,调用类的static方法,实例化一个该类的对象
    • 初始化子类的时候直接引用父类
    • 通过反射获取类的字节码对象
    • jvm启动时被标为启动类的类

    类的被动引用

    • 调用类的static final常量
    • 通过子类调用父类的static变量,static方法(子类被动引用,父类直接引用)
    • 通过数组来引用类

    执行引擎

    作用:用来执行java生成的.class字节码,解析编译成各种cpu能执行的二进制指令

    主要组件

    解释器:Java虚拟机启动时,会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容编译为对应平台的机器指令执行。

    即时编译器:虚拟机将字节码直接编译成和本地机器平台相关的机器语言。

    • 中间代码生成器:生成中间代码
    • 代码优化器:负责优化上面生成的中间代码
    • 目标代码生成器:负责生成机器代码或本地代码
    • 分析器:一个特殊组件,负责查找热点,即该方法是否被多次调用;

    垃圾收集器:用来回收垃圾 

    JVM内存模型

    含义:在java运行时jvm虚拟机拿到自己能支配的的内存之后将内存进行了割分,分为了5部分,分别为栈区、堆区、方法区、本地方法栈、程序计数器 

    JVM内存结构

    • 堆(Heap):jvm管理内存中最大的一块,可以被所有线程共享,在虚拟机启动时创建,此内存区域的唯一目的就是存放变量的实例,几乎所有对象的实例都在这里分配内存
    • 方法区(Method Area):其与堆一样都是各个线程共享的一块区域,它用于储存已被虚拟机加载的类的信息,常量,静态变量,即时编译器编译的代码等数据
    • jvm栈(stacks):其是线程私有的,他的生命周期与线程相同,线程结束,栈内存也就释放了。jvm栈描述的是java方法执行的内存模型,每个方法在执行的时候都会同时创建一个栈帧,用于储存局部变量表,操作栈,动态链接,方法出口等信息,每一个方法被调用直至执行完成的过程就对应着一个栈帧在jvm栈中从入栈到出栈(先进后出)的过程
    • 本地方法栈(native method stacks):其与jvm栈发挥的作用非常相似,区别不过是jvm栈为虚拟机执行java方法服务,而本地方法栈则是为jvm使用native(原生)方法服务
    • 程序计数器(program counter register):其是一块较小的内存空间,指向程序当前运行的位置,可以看作当前线程所执行的字节码的行号指示器

    垃圾回收机制与内存泄漏

    垃圾回收机制:主要是对内存的释放,因为在创建对象时要申请一个空间。

    1. 不再使用的内存空间应回收
    2. java消除了程序员回收无用内存空间的职责,提供了一种系统级别线程跟踪储存空间的分配状况,在JVM的空闲时,检查并释放可被释放的存储空间。
    3. 垃圾收集在java程序运行中自动进行,程序员无法精确控制干预。
    4. GC的自动回收提高了内存空间的使用效率,也提高了编程人员的利用效率,减少了因为没有释放空间而导致的内存泄露。

    内存泄漏:内存一直被占着却不能使用。

    GC(garbage collection)的分类

    1. 自动GC:一般在JVM内存不足时由JVM系统自动对内存进行垃圾回收
    2. 手动GC:手动的进行内存的分配和释放,如果忘记释放,对应的内存不能被再次使用 
    System.gc();  //提醒JVM的垃圾回收执行GC,但是不确定是否马上执行GC

    GC判断策略

    引用计数

    每个对象都有一个引用计数属性,新增一个引用时,计数加一,引用释放时,计数减一,引用计数为0时可以回收,此方法简单,无法解决循环依赖问题。

    • 绿色云朵是内存中的根对象,表示程序中正在使用的对象
    • 蓝色圆圈是内存中的活动对象,其中的数字表示其引用计数
    • 灰色圆圈是内存中没有活动对象引用的对象,表示非活动对象 

    • 红色对象指实际上应用程序使用不到的垃圾对象但由于引用计数的限制,仍然存在内存泄漏 。

    可达性分析(解决循环依赖问题)

    从GC根节点开始向下搜索,搜索走过的路径称为引用链。当一个对象到GC根节点没有任何引用链相连时则证明此对象是不可用的不可达对象。

    注意:由于引用计数算法不能解决循环依赖问题,JVM并没有使用引用计数算法作为GC中判断对象是否存活的算法,用的是可达性分析。 

    GC收集算法

    复制算法

    将可用的内存按照容量分为大小相等的两块,每次只使用其中的一块,这一块内存用完时就将还存活的对象复制到另一块上面去,然后把已经用过的内存空间一次清理掉(如此循环往复)。 

    标记清除算法

    标记阶段:从根节点开始标记所有的可达对象,未被标记的为垃圾对象。

    清除阶段:清除所有未被标记的对象

    注意:标记清除算法容易产生内存碎片(就是内存有,但是相对太小不能用) 

    标记整理算法

    标记阶段:从根节点开始标记所有的可达对象,未被标记的为垃圾对象。

    整理阶段:将所有存活的对象压缩到内存的一端,之后清理边界外的所有空间。

    分代垃圾收集 

     java采用了分代垃圾收集,这种方法会跟java对象生命周期将堆内存划分为不同的区域,在垃圾收集过程中可能会将对象移到不同的区域。

    JVM内存(堆里)

    新生代

    • 伊甸园区
    • 存活区

    老生代

    注意:

    • 伊甸园区与存活区内存大小为8:1
    • 存活区有2块
    • GC垃圾回收主要是在伊甸园区和养老区
    • 新生代内存不足时会触发MinorGC,老生代内存不足时会触发FullGC 
    • FullGC的速度一般会比MinorGC的速度慢10倍以上(其也会回收方法区)

    关于新生代与老生代的理解 

    一般情况下新建的对象均会被分配到伊甸园区(一些大对象特殊处理)这些对象经过第一次gc后,如果仍然存活将会被迁移到存活区,如果存活区有数据的部分和伊甸园区都满了,那么就会发生一次MinorGC,将没有被清理的对象移到另一块存活区如此循环往复。存活区启用复制算法15次的对象如果仍然存活那么就会直接放入老生代,大对象直接进入老生代(大对象指的是需要大量连续内存空间的对象),这样做的目的是避免在伊甸园区和两个存活区之间发生大量的内存拷贝。老生代如果内存也满了那么就会发生一次FullGC

    回收方法区

    方法区的回收价值很低,主要回收废弃的常量和无用的类

    如何判断无用的类

    • 该类的所有实例都被回收(java堆中没有该类的对象)
    • 加载该类的classloader已经被回收
    • 该类对应的java.lang.class对象没有任何地方被引用,无法在任何地方通过反射访问类

  • 相关阅读:
    Linux安全实验——开关机安全控制
    运放-电压采集采样电路设计
    【Serilog】具有完全结构化事件的简单.NET日志记录
    PHP isset()和empty()区别
    网络基本概念
    如何使用分治算法的思想,分治技巧详解
    【Mysql】 linux | mysql | 配置日志路径 | 关闭binlog
    滑动窗口算法题
    opencv图像处理(3)
    【热力】基于matlab模拟矩形板上二维温度分布
  • 原文地址:https://blog.csdn.net/m0_60027772/article/details/126185358