JVM 基础、GC 算法与 JProfiler 监控工具详解

目录

1、引言

[1.1 JVM内存与本地内存](#1.1 JVM内存与本地内存)

[1.2 JVM与JDK的关系](#1.2 JVM与JDK的关系)

2、JVM基础

[2.1 JVM(Java Virtual Machine)](#2.1 JVM(Java Virtual Machine))

[2.2 Java与JVM的关系](#2.2 Java与JVM的关系)

[2.3 JVM的内存结构](#2.3 JVM的内存结构)

[2.3.1 堆内存](#2.3.1 堆内存)

[2.3.2 栈内存](#2.3.2 栈内存)

[2.3.3 方法区](#2.3.3 方法区)

[2.3.4 本地方法栈](#2.3.4 本地方法栈)

[2.3.5 程序计数器(PC寄存器)](#2.3.5 程序计数器(PC寄存器))

[2.4 JVM的位置](#2.4 JVM的位置)

[2.5 JVM的体系结构](#2.5 JVM的体系结构)

[2.6 JVM调优](#2.6 JVM调优)

3、类加载器及双亲委派机制

[3.1 类加载器的作用](#3.1 类加载器的作用)

[3.2 常见的类加载器](#3.2 常见的类加载器)

[3.3 双亲委派机制](#3.3 双亲委派机制)

[3.4 沙箱安全机制](#3.4 沙箱安全机制)

4、Native方法与JNI(Java本地接口)

[4.1 Native关键字](#4.1 Native关键字)

[4.2 JNI的作用](#4.2 JNI的作用)

5、程序计数器(PC寄存器)

[5.1 定义](#5.1 定义)

[5.2 作用](#5.2 作用)

6、方法区

[6.1 定义](#6.1 定义)

[6.2 特点](#6.2 特点)

7、Java栈与堆内存

[7.1 栈](#7.1 栈)

7.1.1数据结构

[7.1.2 线程独立](#7.1.2 线程独立)

[7.1.3 栈溢出](#7.1.3 栈溢出)

[7.2 堆内存](#7.2 堆内存)

[7.2.1 堆的结构](#7.2.1 堆的结构)

[7.2.2 垃圾回收](#7.2.2 垃圾回收)

[7.2.3 堆内存溢出(OOM)](#7.2.3 堆内存溢出(OOM))

[7.2.4 堆内存的调优](#7.2.4 堆内存的调优)

[7.2.5 内存分配策略](#7.2.5 内存分配策略)

[8、 默认垃圾回收算法](#8、 默认垃圾回收算法)

[8.1 分代垃圾回收](#8.1 分代垃圾回收)

[8.2 垃圾回收器的选择](#8.2 垃圾回收器的选择)

[8.3 总结](#8.3 总结)

9、使用JProfiler工具分析OOM原因

[9.1 OOM(OutOfMemoryError)分析](#9.1 OOM(OutOfMemoryError)分析)

[9.2 工具介绍](#9.2 工具介绍)

[9.3 在IDEA中使用JProfiler](#9.3 在IDEA中使用JProfiler)

10、GC常见的算法

[10.1 标记整理(标记压缩)](#10.1 标记整理(标记压缩))

[10.2 标记清除法](#10.2 标记清除法)

[10.3 复制算法](#10.3 复制算法)

[10.4 引用计数器](#10.4 引用计数器)

[10.5 GC算法总结](#10.5 GC算法总结)

[10.6 GC算法与分代收集](#10.6 GC算法与分代收集)

11、Java内存模型(JMM)


1、引言

1.1 JVM内存与本地内存

  • JVM内存 vs. 本地内存:

    在JVM中操作时,主要是使用JVM虚拟机的内存,这包括了堆内存(Heap)、方法区(Method Area)、栈内存(Stack)、本地方法栈(Native Method Stack)等。JVM负责管理和分配这部分内存。

    • 堆内存:用于存储对象实例,这是JVM主要使用的内存区域。
    • 栈内存:用于存储每个线程的局部变量、方法调用链等。
    • 方法区:用于存储类信息、常量、静态变量等。
    • 本地方法栈:用于调用本地(native)方法时使用的内存。

    JVM操作的这些内存都在其管理之下,虽然它运行在操作系统的本地环境中,但开发者一般不直接与操作系统的内存打交道。JVM虚拟内存是在操作系统的本地内存之上管理的。简单来说,JVM虚拟机的内存是操作系统为它分配的内存的一部分,它在此基础上进一步划分和管理。

1.2 JVM与JDK的关系

  • JVM和JDK的关系:

    当下载并安装JDK(Java Development Kit)时,JVM(Java Virtual Machine)就会自动包含在内。JVM是JDK中的一个核心组件,它负责执行Java字节码。JDK不仅包含JVM,还包括编译器(javac)、标准类库等开发工具。所以只要你安装了JDK,JVM就已经包含在你的环境中了,不需要单独下载。

2、JVM基础

2.1 JVM(Java Virtual Machine)

JVM是Java虚拟机,是运行Java程序的基础环境。它负责加载、执行Java字节码,并提供内存管理、垃圾回收等功能。Java的跨平台性正是由于JVM的存在,因为Java程序在不同操作系统上的JVM上运行,从而实现了"写一次,处处运行"的特性。

2.2 Java与JVM的关系

  • Java编译过程:Java源代码首先会通过编译器(如javac)编译成字节码(.class文件)。这个字节码并不直接运行在物理机器上,而是由JVM来解释和执行。
  • JVM的作用:JVM是Java程序运行时的核心,它接收和解释Java字节码,并将其转换为操作系统可执行的机器指令。通过JVM,Java程序可以跨平台运行,即同一份Java代码可以在不同操作系统上运行。

2.3 JVM的内存结构

官方JVM体系结构图

简易结构图

JVM内存结构分为多个区域,每个区域负责管理不同类型的数据和任务:

  • JVM垃圾介绍:
    • 堆内存中的垃圾:堆中存储所有对象实例,由于Java的自动内存管理机制,垃圾回收器(GC)会负责回收不再使用的对象,这些未使用的对象即为"垃圾"。
    • 方法区中的垃圾:虽然方法区存储类信息和常量,但在某些情况下,未被使用的类或静态数据也会被回收,因此方法区本质上也可能产生垃圾。
    • 栈内存中的数据:Java栈、本地方法栈和程序计数器中存储的是临时数据,比如局部变量、方法调用信息等。由于这些区域的生命周期与方法的执行周期相关,方法执行完毕后栈帧会自动释放,因此不会有垃圾存在。
2.3.1 堆内存
  • 堆(Heap):用于存储所有Java对象及数组。堆是垃圾回收器(GC)的主要工作区域,因为在这个区域中会产生垃圾对象。
2.3.2 栈内存
  • Java栈(Java Stack):存储局部变量、方法调用栈帧等。每个线程都有自己独立的Java栈,栈中不会存在垃圾对象,因为栈帧随方法执行结束自动释放。
2.3.3 方法区
  • 方法区(Method Area):存储已加载的类信息、常量、静态变量、即将编译的代码等。方法区本质上是堆内存的一部分,但用于特定用途。
2.3.4 本地方法栈
  • 本地方法栈(Native Method Stack):专门用于调用本地方法(使用非Java语言编写的方法,如C或C++),类似于Java栈。
2.3.5 程序计数器(PC寄存器)
  • 程序计数器(PC寄存器):存储当前线程执行的字节码的地址(程序指令),用于跟踪线程的执行进度。

2.4 JVM的位置

JVM运行在操作系统之上,充当了Java程序和底层操作系统之间的中间层。JVM实际上是一个软件,其主要实现通常是使用C语言编写的,部分实现还可能使用其他低级语言(如汇编)来优化性能。JVM负责屏蔽不同操作系统的细节,使Java程序能够在多种平台上运行。

2.5 JVM的体系结构

JVM的体系结构包括多个组成部分,主要分为以下几个模块:

  • 类加载器系统(Class Loader Subsystem):负责加载.class文件,验证、准备、解析和初始化类。
  • 运行时数据区(Runtime Data Area):包括堆、栈、方法区等,用于管理程序执行时的各种数据。
  • 执行引擎(Execution Engine):负责解释执行字节码或将字节码编译为本地机器码。
  • 本地方法接口(Native Interface):用于调用非Java编写的本地方法。
  • 垃圾回收系统(Garbage Collection System):自动管理堆中的对象回收。

2.6 JVM调优

JVM的性能优化通常主要针对堆和方法区进行。因为这两个区域负责管理Java对象、类信息和静态数据,并且会产生垃圾。通过调整堆的大小、垃圾回收策略、类加载和卸载等,可以提高应用的性能和内存使用效率。

  • JVM调优的几个常见方面:
    • 堆大小设置:通过-Xms-Xmx参数设置堆的初始大小和最大大小。
    • 垃圾回收器选择:不同的GC算法适合不同的场景,如G1适用于大内存应用,CMS适用于低延迟应用。
    • 方法区调优:通过调整方法区的大小和类卸载策略,防止内存溢出或类加载过多导致的性能问题。

3、类加载器及双亲委派机制

  • 类加载器及双亲委派机制:确保类加载的安全性和有效性,通过双亲委派避免核心类被恶意替换。

3.1 类加载器的作用

类加载器(ClassLoader)负责将.class文件加载到JVM中并转化为内存中的Class对象。具体来说,当你创建一个对象(如new Student())时,类加载器会加载该类的字节码文件,而引用对象存放在栈中,实际的对象数据存储在堆内存中。

3.2 常见的类加载器

JVM中存在多种类加载器,它们按层次结构组织,负责加载不同范围的类:

  1. 虚拟机自带的加载器:由JVM内部实现,用于加载JDK核心类。
  2. 启动类加载器(Bootstrap ClassLoader):又称为根类加载器,负责加载JVM核心库,通常是$JAVA_HOME/lib中的类,如rt.jar
  3. 扩展类加载器(Extension ClassLoader):用于加载扩展库,通常是$JAVA_HOME/lib/ext目录下的类。
  4. 应用程序类加载器(Application ClassLoader):负责加载用户类路径中的类,通常加载classpath中的类,是默认的类加载器。

3.3 双亲委派机制

双亲委派机制是一种类加载器的工作方式,用来避免类重复加载并保证Java核心类的安全性。

双亲委派机制的执行过程如下:

  1. 类加载请求:当某个类加载器接收到一个类的加载请求时,它不会立即尝试加载,而是将请求委派给它的父类加载器去处理。
  2. 逐级向上委派:每个类加载器都会将请求向上委托给父类加载器,直到最顶层的启动类加载器(Bootstrap ClassLoader)。
  3. 启动类加载器检查:启动类加载器检查是否能够加载该类,如果能,则加载并返回;否则将请求传递给子类加载器。
  4. 子类加载器处理:如果启动类加载器无法加载,才由子类加载器尝试加载。

注意:双亲委派机制的一个常见问题是如果我们定义了一个与JDK核心类(如java.lang.String)同名的类,它将永远不会被加载。因为启动类加载器会优先加载java.lang.String类,导致我们的自定义类无法加载,进而报错,如找不到main方法。

通过obj.getClass().getClassLoader()方法可以获取对象的类加载器,利用反射可以深入了解类加载器的工作机制。

3.4 沙箱安全机制

沙箱机制是一种保护机制,限制程序运行时的系统资源访问,防止恶意代码执行或访问敏感资源。类加载器在沙箱机制中扮演了重要角色,特别是采用了双亲委派机制,防止用户定义的恶意类替代核心类。

沙箱安全机制的组成:

  • 字节码校验器:确保字节码的正确性和安全性。
  • 类加载器:采用双亲委派机制防止类加载冲突。
  • 存取控制器:控制程序对文件、网络等系统资源的访问。
  • 安全管理器:提供更细粒度的安全策略控制。
  • 安全软件包:实现Java平台的安全功能,如加密、认证等。

4、Native方法与JNI(Java本地接口)

  • Native方法与JNI:允许Java调用底层代码,通过JNI扩展Java的使用范围。

4.1 Native关键字

native关键字用于声明本地方法,即用非Java语言(如C/C++)实现的方法。这些方法是因为Java无法直接实现某些底层操作,必须调用操作系统或底层库来完成。native方法调用时,会进入本地方法栈,并通过JNI(Java Native Interface,Java本地接口)与底层代码交互。

4.2 JNI的作用

JNI是Java与其他语言(如C/C++)进行交互的桥梁,主要作用是扩展Java的能力,让Java可以调用非Java的底层代码,尤其是在需要高性能或者与系统底层紧密相关的场景下(如硬件交互、系统调用等)。

5、程序计数器(PC寄存器)

  • PC寄存器:用于线程的字节码指令跟踪,是实现多线程的关键。

5.1 定义

程序计数器(Program Counter Register)是一个非常小的内存区域,每个线程都有一个独立的PC寄存器。它用来存储当前线程正在执行的字节码指令地址,指向下一条即将执行的指令。由于Java是多线程的,PC寄存器是线程私有的。

5.2 作用

程序计数器用于跟踪线程的执行进度,线程切换时通过它恢复到正确的执行位置,因此它是实现Java多线程的关键组件之一。

6、方法区

  • 方法区:用于存储类信息、静态变量、常量等,是JVM内存结构的重要组成部分。

6.1 定义

方法区(Method Area)是JVM内存的一个逻辑区域,它由所有线程共享,主要用于存储:

  • 已加载的类信息(类名、方法、字段等)
  • 运行时常量池(存储编译器生成的常量)
  • 静态变量
  • 类的字节码和方法(包括构造方法、接口定义等)

6.2 特点

  • 共享区域:方法区是线程共享的内存空间,不像栈和PC寄存器那样是线程私有的。
  • 存储内容:主要存储类的元数据、静态变量、常量池等,而对象实例是存储在堆中的。
  • 垃圾回收:尽管方法区存储静态信息,但在某些情况下(如类卸载)也会触发垃圾回收。

7、Java栈与堆内存

7.1 栈

7.1.1数据结构
  • 数据结构:栈是一种后进先出(LIFO,Last In First Out)的数据结构,只有栈顶的元素可以被访问和修改。
7.1.2 线程独立
  • 线程独立:每个线程都有自己的栈,栈的生命周期与线程相同。
  • 栈内存内容:
    • 八大基本数据类型(如int, char等)
    • 对象引用:指向堆中实际对象的引用
    • 方法调用:每当一个方法被调用时,相关信息(如局部变量、参数等)会存放在栈中。
7.1.3 栈溢出
  • 栈溢出

    栈溢出(Stack Overflow)通常发生在递归调用中,如果方法循环调用而没有终止条件,最终会耗尽栈空间,导致溢出错误。

7.2 堆内存

  • 堆内存

    • 单一堆:一个JVM实例只有一个堆内存,所有线程共享这个堆。
    • 堆内存的调节:可以通过JVM参数进行配置,通常会调整堆的初始大小和最大大小。
  • JVM实现

    三种常见的JVM实现:

    1. Sun公司的HotSpot
    2. BEA的JRockit
    3. IBM的J9 VM

    注意:从JDK 1.8开始,永久代(PermGen)被移除,改为使用元空间(Metaspace)来存储类的元数据。

7.2.1 堆的结构
  • 堆的结构

    堆内存通常被细分为几个区域:

    1. 新生区(Eden Space):
      • 对象在此区域被创建(使用new关键字)。
      • 如果新生区满了,会触发轻量级垃圾回收(轻GC)。
    2. 幸存者区(Survivor Space,分为S0和S1):
      • 新生区经过一次GC后存活的对象会被转移到这里。经过几次GC后仍然存活的对象可能被转移到老年代。
    3. 老年区(Old Generation):
      • 存放长时间存活的对象,通常是经历过多次GC的对象。
    4. 元空间(Metaspace):
      • 存放类的元数据,从JDK 1.8开始取代永久代。元空间使用本地内存,物理上不存在限制。
7.2.2 垃圾回收
  • 垃圾回收
    • 轻量级垃圾回收(轻GC):主要针对新生区的对象。
    • 重量级垃圾回收(重GC):通常针对老年区的对象,处理复杂和耗时。
7.2.3 堆内存溢出(OOM)
  • 堆内存溢出(OOM)

    • 当堆内存不足以满足对象创建需求时,会抛出OutOfMemoryError(OOM)。

    • 常见原因包括创建过多的对象,尤其是字符串等长生命周期的对象。

    • 图:

  • 字符串处理

    • 在Java中,字符串的长度是动态的,但受限于可用内存。大量字符串或过长字符串会导致堆内存溢出。
7.2.4 堆内存的调优
  • 堆内存的调优
    • 新生区调优:设置合适的大小以优化轻GC的频率和效率。
    • 老年区调优:通过调整老年区大小来减少重GC的次数
7.2.5 内存分配策略
  • JVM默认情况下分配的总内存通常是计算机内存的1/4,而初始化内存是1/64。可以通过命令行参数调整这些值。

  • 默认分配值(idea代码)

    java 复制代码
        public static void main(String[] args) {
            
            long max = Runtime.getRuntime().maxMemory();
            long total = Runtime.getRuntime().totalMemory();
    
            System.out.println("max" + max + "字节\t" + (max / (double) 1024 / 1024) + "MB");
            System.out.println("total" + total + "字节\t" + (total / (double) 1024 / 1024) + "MB");
        }
  • IDEA中进行JVM调参

  • 参数调优:JVM调优就是在这边地方调优。(调优区别:原本241MB内存大小,调成了981MB)

  • 参考:原始参数,下图是调优后的参数。(避免堆内存OOM错误)

    调优参数: -Xms1024m -Xmx1024m -XX:+PrintGCDetails

  • 控制台中:新生区(伊甸园)、老年区、元空间

  • 总结
    • 栈用于存储方法调用和局部变量,每个线程都有独立的栈。
    • 堆用于存储对象,所有线程共享一个堆,内存分为新生区、幸存者区、老年区和元空间。
    • 垃圾回收的策略影响对象的生存周期与内存使用效率。
    • 堆内存的调优对于大型应用性能至关重要,合理配置内存可以有效防止OOM错误。

在 Java 中,垃圾回收器(GC,Garbage Collector)主要负责自动管理内存的分配和释放。Java 虚拟机(JVM)中的垃圾回收机制是基于 分代回收 理念的,这种机制认为对象的生命周期是不一样的,因此可以根据对象存活的时间长短采用不同的回收策略。

8、 默认垃圾回收算法

Java 的垃圾回收器默认使用 HotSpot VM,它采用的垃圾回收算法是 分代垃圾回收算法(Generational Garbage Collection),结合了多种不同的垃圾回收算法,具体包括:

  • 新生代回收算法:复制算法(Copying Algorithm)
  • 老年代回收算法:标记-清除算法(Mark-Sweep Algorithm)和 标记-整理算法(Mark-Compact Algorithm)

8.1 分代垃圾回收

  • 新生代:大多数新创建的对象会被分配在新生代。新生代中的对象大部分都是"朝生夕死"的,因此新生代的垃圾回收主要使用 复制算法,将存活对象从一个区域复制到另一个区域,非存活对象则直接被清理。

    • Eden 区:新对象首先分配在 Eden 区。
    • Survivor 区:对象在 Eden 区中经过一次垃圾回收后,如果没有被清理,会被移动到 Survivor 区。

    算法: 新生代中主要使用 复制算法(Copying Algorithm),它将存活的对象复制到另一个区域,而非存活的对象则会被回收。这种方法回收速度很快,因为它不需要遍历所有对象,只需处理存活的对象。

  • 老年代:新生代中经过多次垃圾回收后仍然存活的对象会被晋升到老年代。老年代中的对象通常存活时间较长,因此回收频率较低。

    算法:

    • 标记-清除算法(Mark-Sweep Algorithm):先标记出所有存活的对象,然后清理掉未标记的对象。缺点是容易产生内存碎片。
    • 标记-整理算法(Mark-Compact Algorithm):在标记完存活对象后,不仅清理掉未存活的对象,还会将存活对象整理到一起,避免内存碎片问题。

8.2 垃圾回收器的选择

虽然 Java 默认使用的是分代回收机制,但具体使用的垃圾回收器可以根据 JVM 的配置不同而变化,以下是常见的几种垃圾回收器:

  • Serial GC:一个简单的单线程垃圾回收器,适合单核 CPU 和小型应用。
  • Parallel GC:多线程垃圾回收器,适合多核 CPU 和注重吞吐量的应用。
  • G1 GC:一种面向服务端应用的垃圾回收器,旨在减少 GC 造成的停顿时间,替代了老年代的 CMS(Concurrent Mark-Sweep)回收器。
  • ZGC 和 Shenandoah GC:专注于极低停顿时间的垃圾回收器,适用于超低延迟应用。

8.3 总结

Java 默认采用的是基于 分代垃圾回收算法 的策略,其中新生代主要使用 复制算法,而老年代结合 标记-清除 和 标记-整理 算法。这种分代回收机制优化了内存管理,使得不同生命周期的对象能被高效回收。

9、使用JProfiler工具分析OOM原因

9.1 OOM(OutOfMemoryError)分析

  • OOM(OutOfMemoryError)分析
    • 内存快照分析:使用内存快照工具(如JProfiler、MAT)可以方便地查看在OOM发生时的内存状态,定位具体出错的代码行。
    • 代码行定位:通过内存快照分析,可以快速找到导致内存泄漏或堆溢出的代码位置,避免逐行调试的麻烦。

9.2 工具介绍

  • 工具介绍
    • MAT(Memory Analyzer Tool):用于分析Java堆转储文件,帮助定位内存泄漏、获取堆中对象的数据、找到占用内存大的对象等。
    • JProfiler:强大的性能分析工具,能够监控应用的内存使用情况,生成内存快照并分析内存泄漏。(实时监控)

9.3 在IDEA中使用JProfiler

  1. 插件安装:在IDEA中安装JProfiler插件,可以集成性能分析和监控功能。

  2. JProfiler官网上下载

  3. 编写OOM异常代码

    • 在IDEA中编写可能导致OOM的代码,利用JProfiler监控具体行数:

      java 复制代码
      public class OOMExample {
          public static void main(String[] args) {
              List<String> list = new ArrayList<>();
              while (true) {
                  list.add("This is a very long string that will keep consuming memory...");
              }
          }
      }
  4. 调参

    1. -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
    • -Xms:设置JVM的初始内存分配大小,建议设置为总内存的1/64(例如:-Xms512m)。

    • -Xmx:设置JVM的最大内存分配大小,建议设置为总内存的1/4(例如:-Xmx2048m)。

    • -XX:+PrintGCDetails:打印垃圾回收的详细信息,帮助分析内存使用情况和垃圾回收的效果。

  5. 监控软件使用

    • 配置

      • -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\Sachsen\Desktop\testKZ -Xms512m -Xmx512m
    • 代码

      java 复制代码
          public static void main(String[] args) {
      
              String str = "zhangSan";
              while (true) {
                  str += str + new Random().nextInt(1888888888) + new Random().nextInt(999999999);
              }
          }
    • java.lang.OutOfMemoryError

    • 配置的文件目录下生成快照

    • 查看当前对象集

    • 查看栈溢出的对象(第一种方式)Current Object Set对象集传递引用查看

    • 查看栈溢出的对象(第二种方式)Current Object Set对象集图像结构查看

    • 查看栈溢出的对象(查看的第三种方式)Thread Dump线程转储查看

      • 线程转储记录了快照时的所有线程,可以查看线程运行状态(运行、等待、阻塞等)和线程调用栈(展现线程执行的方法调用路径)。运行出现错误的线程会用一个特殊的图标标记。
  • 总结
    • 使用JProfiler和MAT等工具可以快速定位和分析OOM的原因,减少线下调试的时间。
    • 合理配置JVM内存参数有助于防止内存溢出。
    • 对于GC的了解能够帮助优化应用的内存使用和性能。

10、GC常见的算法

10.1 标记整理(标记压缩)

  • 概念:首先标记活着的对象,然后将它们整理到内存的一端,释放出连续的空闲空间。
  • 优点:减少了内存碎片,使内存使用更高效。
  • 缺点:需要额外的时间来移动对象。

10.2 标记清除法

  • 概念:标记所有活着的对象,然后直接清除未标记的对象。
  • 优点:实现简单,不需要额外空间。
  • 缺点:存在内存碎片,可能导致内存浪费。

10.3 复制算法

  • 概念:将存活的对象从Eden区复制到两个幸存者区中的一个(To区),当进行垃圾回收时,清空Eden区。
  • 工作过程:
    1. Eden区:每次GC时,将Eden区的存活对象复制到幸存者区的To区。
    2. 养老区:对象经历一定次数的GC(默认15次)后,若仍存活,则被转移到老年区。
  • 优点:内存利用率高,适合存活时间较短的对象。
  • 缺点:Eden和幸存者区可能会产生空闲区域,浪费内存。

10.4 引用计数器

  • 概念:为每个对象维护一个计数器,记录引用该对象的数量。当计数器为0时,自动回收对象。
  • 优点:简单易实现。
  • 缺点:无法处理循环引用,效率低,不推荐使用。

10.5 GC算法总结

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

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

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

10.6 GC算法与分代收集

  • GC算法:通过不同的策略管理内存,优化性能与资源利用。
  • GC算法也称为分代收集算法,因为它利用了对象生命周期的特性,将对象分为新生代(Eden和幸存者区)和老年区。

11、Java内存模型(JMM)

  • JMM:确保多线程编程中内存访问的一致性和安全性。
  • 定义:Java内存模型规定了Java程序中线程如何访问共享内存,确保线程间的可见性与一致性。
  • 主要内容:
    • 主内存与工作内存:JMM将内存划分为主内存(共享内存)和工作内存(线程的私有内存)。
    • 内存屏障:确保特定的操作顺序和内存可见性。
    • 可见性:确保一个线程对共享变量的修改能被其他线程看到。
    • 原子性:保证对共享变量的操作是不可分割的。
相关推荐
懒惰才能让科技进步29 分钟前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
Ni-Guvara43 分钟前
函数对象笔记
c++·算法
泉崎1 小时前
11.7比赛总结
数据结构·算法
你好helloworld1 小时前
滑动窗口最大值
数据结构·算法·leetcode
AI街潜水的八角2 小时前
基于C++的决策树C4.5机器学习算法(不调包)
c++·算法·决策树·机器学习
白榆maple2 小时前
(蓝桥杯C/C++)——基础算法(下)
算法
JSU_曾是此间年少2 小时前
数据结构——线性表与链表
数据结构·c++·算法
此生只爱蛋3 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
咕咕吖4 小时前
对称二叉树(力扣101)
算法·leetcode·职场和发展
九圣残炎4 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode