JVM入门必备

2023-11-14

1、JVM 的位置

在这里插入图片描述

2、JVM 的体系结构

JVMJava虚拟机)是Java程序的运行环境,它对于Java平台的运行和跨平台特性的实现有着重要的作用。JVM的体系结构有以下几个部分:

  • 类加载器(ClassLoader):负责将 .class字节码文件加载到内存中,并生成对应的Java类。

  • 运行时数据区(Runtime Data Area):包括了方法区、堆、虚拟机栈、本地方法栈和程序计数器等五个部分。

    • 方法区:用于存储类的信息,如静态变量、成员变量、常量池等。在Java 8以后,方法区被元空间(Metaspace)所代替。
    • 堆:用于存储对象实例,包括数组和其他对象实例。堆被所有线程共享,同时也是垃圾回收器的重点关注对象。
    • 虚拟机栈(Java Stack):用于存储线程执行方法的栈帧(Frame)。每个栈帧对应一个Java方法的执行,栈帧存储局部变量、操作数栈、动态连接和方法出口等信息。
    • 本地方法栈(Native Stack):本地方法栈类似于虚拟机栈,不过它是为了执行Native方法服务,此处Native方法指的是使用非Java语言编写的方法。
    • 程序计数器(Program Counter Register):当前线程所执行的字节码所在的位置指示器,在多线程时每个线程都有自己独立的程序计数器。
  • 执行引擎(Execution Engine):负责解释字节码,并将字节码翻译成机器码,使程序能够在本地机器上运行

  • 本地方法接口(Native Interface):JVM提供了本地方法接口,允许Java程序调用其他语言写成的程序。

在这里插入图片描述

总的来说,JVM是一个在本地机器上运行的进程,并且它是Java跨平台特性的核心实现。JVM的体系架构在使用Java程序开发的过程中是至关重要的,了解其内部结构有助于更好地理解Java程序是如何运行的。

3、类加载器

1️⃣ 什么是类加载器?

类加载器(Class Loader)是Java虚拟机(JVM)中的一个子系统,它的功能是将编译好的Java代码(.class文件)加载到内存中,在运行时动态地生成类,并将其连接到JVM运行状态之中

Java中,程序需要在运行时动态地加载一些类,然而这些类在编写程序的时候可能并不确定,或者需要从网络或其他地方加载,因此有了类加载器作为动态加载类的一个重要工具

Java中的类加载器采用了一种双亲委派模式的机制。当一个类需要被加载到JVM中时,首先由Bootstrap类加载器(根类加载器)尝试加载该类,如果找不到该类,就将请求传递给Extension类加载器(扩展类加载器),如果Extension类加载器还是找不到该类,则将请求传递给Application类加载器(应用程序类加载器)进行加载。如果Application类加载器仍然无法找到该类,则该类将被认为是不存在,JVM会报ClassNotFoundException
在这里插入图片描述

2️⃣ 具体来说,类加载器的工作流程如下:

  • 装载:查找并加载指定的**.class字节码文件,生成一个对应的Class**对象。
  • 连接:将静态的字节码文件中的符号引用替换成直接引用,包括验证、准备、解析三个阶段。
  • 初始化:对类进行初始化,执行类构造器函数()。

需要注意的是,类加载器除了加载类文件并生成Class对象之外,还有一些其他的职责,例如缓存已经加载的Class对象、保证类加载器的隔离性以及保护Java代码的安全性

3️⃣ Class 对象是抽象的,使用同一个类的是相同的模板,但实例对象是具体的

package com.kuangshen;

/**
 * @author Bonbons
 * @version 1.0
 * 查看实例对象、Class文件、ClassLoader的关系
 */
public class Car {
    public static void main(String[] args) {
        Car car1 = new Car();
        Car car2 = new Car();
        Car car3 = new Car();

        // 输出他们的 HashCode
        System.out.println(car1.hashCode()); // 325040804
        System.out.println(car2.hashCode()); // 1173230247
        System.out.println(car3.hashCode()); // 856419764

        // 获取他们的字节码文件,证明属于一个模板
        Class<? extends Car> aClass1 = car1.getClass();
        Class<? extends Car> aClass2 = car2.getClass();
        Class<? extends Car> aClass3 = car3.getClass();

        System.out.println(aClass1.hashCode()); // 1836019240
        System.out.println(aClass2.hashCode()); // 1836019240
        System.out.println(aClass3.hashCode()); // 1836019240
    }
}

4、双亲委派机制

1️⃣ 什么是双亲委派机制?

双亲委派机制(Parent Delegation Model)是Java类加载器的一种工作机制。它是一种由下至上的查找类的过程,通过一层层的委托,最终在顶层的类加载器中寻找类的定义

具体来说,当一个类加载器需要加载一个类时,它首先会判断该类是否已经被加载过了。如果已经被加载过了,则直接返回已加载过的类;否则,该类加载器就会委托父类加载器去加载该类。 如果父类加载器仍无法找到该类,那么就依次递归委托其父类加载器,直至到达最顶层的启动类加载器。如果启动类加载器仍没能加载该类,就会抛出 ClassNotFoundException

2️⃣ 这种双亲委派机制的工作原理有以下优点:

  • 避免重复加载:由于每个类加载器都能够独立地加载类,如果没有双亲委派机制的限制,不同的类加载器可能会加载同一个类的不同版本,造成类的重复定义。而有了双亲委派机制,可以避免这种情况的出现。
  • 确保安全性:由于Java中的安全模型是基于类加载器的,不同的类加载器可以用于隔离不同的代码源。双亲委派机制可以保证不同的代码源不会相互干扰,从而确保Java应用的安全性
  • 简化开发:由于子类加载器会委派父类加载器来加载类,开发者可以很少直接涉及类加载器的工作,从而简化了Java应用的开发和部署的过程

需要注意的是,双亲委派只是一个规范,并不是Java虚拟机的强制规定。在特定的场景下,开发者也可以自定义类加载器,并打破双亲委派机制的限制。

3️⃣ 案例演示:

package com.kuangshen;

/**
 * @author Bonbons
 * @version 1.0
 * 演示双亲委派机制:
 * 尽管我们定义了String类
 */
public class String {
    @Override
    public java.lang.String toString() {
        return "Hello";
    }

    public static void main(String[] args) {
        String string = new String();
        System.out.println(string.getClass().getClassLoader());
        System.out.println(string);
    }
}

因为当前String类没有被加载过,所以类加载器向上委托查找,在引导类加载器中发现有String类,但是因为那个String类中没有main方法,所以就会抛出下面的错误

4️⃣ 解释一下出现上面这个现象的原因:

Java程序需要加载一个类时,当前线程的类加载器不会自己直接加载该类,而是首先将加载任务委托给其父类加载器来尝试加载该类

这个“从下到上,从左到右”的顺序是指类加载器之间的层次结构,从下向上表示类加载器的继承关系,从左到右表示同一级别的类加载器之间的加载顺序。例如,在默认情况下,系统类加载器的父类加载器是扩展类加载器,而扩展类加载器的父类加载器则是引导类加载器。因此,当系统类加载器需要加载一个类时,它会首先将加载任务委托给扩展类加载器,如果扩展类加载器不能加载该类,那么它会继续将加载任务委托给引导类加载器。如果所有的父类加载器都无法加载该类,当前线程的类加载器将会尝试自己加载该类

5、沙箱安全机制

沙箱安全机制(Sandbox Security Mechanism)常常被用于Java应用程序的安全性控制中,其目的在于限制不可信代码的行为,保护Java应用程序不受恶意代码的攻击

1️⃣ 什么是沙箱安全机制?

沙箱安全机制是一种限制不可信代码行为的技术,它可以将不可信代码隔离在一个安全执行环境中,保护系统或应用程序的安全。在Java中,沙箱安全机制的实现是通过类加载器、安全管理器和访问控制器等技术来实现的。

2️⃣ 如何实现沙箱安全机制?

首先,Java应用程序通过类加载器将可信代码加载到JVM中,其中可信代码包括Java核心类库和用户编写的代码

然后,安全管理器负责检查应用程序的所有行为,如果遇到需要访问受限资源的行为,就会调用访问控制器来控制这些行为是否可以被执行

最后,JVM会根据安全管理器和访问控制器的规则,限制不可信代码的行为,保证系统或应用程序的安全

3️⃣ 沙箱安全机制的工作原理

(1) 类加载器

类加载器是Java中实现沙箱安全机制的重要组成部分,它的主要作用是根据指定的类名或者.class文件的路径,将类文件装载到JVM中,并生成对应的Class对象。在实现沙箱安全机制时,可以利用类加载器将可信代码和不可信代码分开加载,从而实现代码隔离

(2) 安全管理器

安全管理器是Java中实现沙箱安全机制的核心,它负责检查Java应用程序的所有行为,如文件访问、网络访问、系统属性的读写等,判断这些行为是否合法,并控制其执行

如果Java应用程序需要访问受限资源,如读取文件或者建立网络连接等,安全管理器将对其进行审查,如果该行为是被允许的,就会继续执行该行为,否则就会阻止该行为的执行。

(3) 访问控制器

访问控制器是安全管理器的一个重要附属部分,它负责控制访问受限资源的行为

Java中,访问控制器通过访问控制列表(Access Control List,ACL)来管理受限资源的访问权限

ACL中记录了哪些用户、程序或者代码段可以访问特定的资源,以及如何访问这些资源

Java程序要访问一个受限资源时,访问控制器会检查ACL中是否包含了该程序或代码段的访问权限,如果有,访问控制器就会允许这个程序或代码段访问该受限资源。

总之,沙箱安全机制是Java应用程序安全的重要防护措施。通过类加载器、安全管理器和访问控制器的协同工作,可以有效地限制不可信代码的行为,保护Java应用程序的安全。

4️⃣ 所有的Java应用程序都可以指定沙箱、安全策略,接下来介绍一下发展历程

Java 安全模型的核心就是 Java 沙箱

(1)JDK1.0 安全模型

再 Java 中执行程序分为本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信任的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的 java 实现中,安全依赖于沙箱机制

在这里插入图片描述

(2)JDK1.1安全模型

但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。所以在 1.1 版本中对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限

在这里插入图片描述
(3)JDK1.2安全模型

在 Java1.2 版本中,再次改进了安全机制,增加了代码签名。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。

在这里插入图片描述
(4)JDK1.6安全模型

引入了域的概念,虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分负责与关键资源进行交互,而各个应用域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域,对应不一样的权限。存在于不同域中的类文件就具有有了当前域的全部权限

在这里插入图片描述

6、Native

1️⃣ 了解一下什么是 Native 关键字

在编程中,Native 通常用作关键字来表示某些代码是用其他语言编写的并且已被编译成机器码,以便在当前语言中使用

具体来说,Native 关键字表示一个外部的、非当前语言的函数或方法,通过Native关键字,程序能够直接访问外部函数或方法的实现,并将其结果集成到当前程序中

2️⃣ 我们查看一下 Native 在哪用过:

我们创建线程时,调用的 start 方法中,里面有个 start0 方法,这个方法就是被 native 关键字修饰的

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

3️⃣ 总结:

(1)使用了 native 关键字的,说明 java 的作用范围达不到了,会去调用底层 C 语言的库

(2)会进入本地方法栈,进而调用本地方法的本地接口 JNI(Java Native Interface)

(3)JNI作用:扩展 Java 的使用,融合不同的编程语言为 Java 所用

7、PC寄存器

什么是程序计数器?

程序计数器是计算机处理器中的一种特殊寄存器,用于保存正在执行的指令的地址或指针

在一次程序执行期间,处理器会按照程序设计者编写的指令序列来执行程序。处理器从程序代码的第一条指令开始执行,并且按照指令的顺序一条一条地执行下去。程序计数器的作用就是指示处理器下一条要执行的指令所在的内存地址

当处理器执行完一条指令时,程序计数器的值会自动增加,指向下一条指令的地址。这样处理器就可以继续执行下一条指令。如果程序发生了跳转,比如执行了一个函数调用或错误处理,程序计数器的值也会发生改变
在这里插入图片描述

程序计数器在不同的架构中可能有着不同的命名和实现方式。在大多数现代计算机体系结构中,程序计数器是一种常规寄存器,大小通常是与寻址空间相关,例如在32位架构中,程序计数器通常是32位,而在64位架构中,它通常是64位。

8、方法区

1️⃣ 方法区指的是什么?

Java虚拟机(JVM)中的方法区(Method Area)是一个存储类信息的区域。它存储了类的信息、常量、静态变量、即时编译器编译过的代码等。方法区在JVM的运行时数据区域中属于共享区域,被所有线程所共享

静态变量、常量、类信息(构造方法、接口定义)、运行时常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关

方法区的内存空间是 JVM 中内存分配最大的一块,它的大小可以通过 -XX:MaxMetaspaceSize 参数设置,此参数在JDK 8及以后版本替代了 -XX:PermSize-XX:MaxPermSize 参数。

2️⃣ 方法区的结构主要包括以下几个部分:

  • 运行时常量池:是方法区的一部分,用于存储编译时产生的各种字面量和符号引用。字面量包括字符串、数字常量、类、方法等字面量,符号引用包括类和接口的全限定名、方法和字段的名称和描述符等。
  • 类信息:包括类的全限定名、类的访问修饰符、父类、接口实现等信息。
  • 静态变量:静态变量是在类加载时初始化的,其大小是固定的。
  • 即时编译器(JIT)编译后的代码:JVM中提供的即时编译器是负责将 Java 代码编译成本地代码。JIT 编译器将 Java 字节码翻译成本地机器码后存放在方法区中。

在这里插入图片描述

在运行Java应用程序时,随着类和对象的不断加载和卸载,方法区会发生各种内存溢出和内存泄漏的问题。比如,如果应用程序中定义了大量的字符串常量,且这些字符串常量的生命周期比较长,就有可能导致方法区的内存溢出问题。解决这些问题需要在程序开发和应用部署时加以注意和规划方法区的内存使用。

9、栈

Java虚拟机中的方法栈(Method Stack)是一种为方法调用和返回而分配的内存区域
每个线程在调用方法时都会创建一个对应的方法栈,用于存放方法的局部变量、方法参数、方法返回值和部分调用时的数据

方法栈是基于栈的数据结构,它具有“先进后出”的特性。每次方法调用时,JVM都会在当前线程的方法栈中创建一个新的栈帧(Stack Frame),用于存储该方法的信息,包括方法本身的信息(方法名、访问修饰符、参数列表等)和该方法在调用状态下的局部变量、操作数栈等信息 【当前正在运行的方法一定在栈的顶部】

在这里插入图片描述

方法栈的大小也可以通过 JVM 参数进行设置,其大小受到限制。如果某个线程的方法栈空间已经被填满,就会抛出 StackOverflowError 异常;如果方法栈的大小超出了 JVM 所允许的最大值,就会抛出 OutOfMemoryError 异常【内存泄漏】

在方法调用过程中,每个线程的方法栈都是独立的,互不干扰。当一个线程完成这个方法调用后,它所使用的方法栈会被销毁,从而为该线程的其他方法调用释放空间 【不需要通过GC回收】

要注意的是,方法栈与堆内存中的对象是不同的概念,方法栈主要存储方法调用的相关信息,而不是对象本身。在Java虚拟机中,对象本身存放在堆内存中

10、三种VM

Java 虚拟机(JVM)是运行Java程序的关键组件,它提供了Java程序在不同硬件平台上独立运行的能力

  • Java 虚拟机有以下三种常见实现:

    • Oracle HotSpot VM:是目前最常用的Java虚拟机,在Java SE 6之后成为Java标准版(Java SE)中默认的虚拟机。它是Oracle公司提供的一款性能优秀、功能完备的JVM,包括了即时编译器(JIT)、垃圾回收、调试等多种功能

    • OpenJDK:是由Oracle公司开源的Java开发工具包(JDK)的一个开源实现。OpenJDK包括了Java虚拟机、类库和Java编译器等组件,也可以作为开发Java代码的工具。OpenJDK提供了跨平台的环境,可以在不同操作系统上运行。OpenJDK是许多其他Java虚拟机实现的基础

    • IBM J9 VM:是IBM公司提供的一款Java虚拟机。相比于Oracle HotSpot VM,IBM J9 VM 的特点在于对硬件的优化,以及更好的内存管理、GC 算法、动态编译器等。IBM J9 VM 通常被用于企业级应用中,因其更高的稳定性、扩展性以及对Java EE等规范的良好支持

除了上述三种常见的Java虚拟机外,还有一些其他的实现,如Azul Zing VM、BEA JRockit VM、Excelsior JET等等,这些虚拟机都具备不同的特点和适用场景

11、堆

1️⃣ 介绍一下 JVM 中的堆的作用

Java虚拟机(JVM)中的堆(Heap)是一个运行时数据区,用于存储Java程序中的对象
所有对象都在堆中分配内存,而且堆中的内存是在JVM启动时分配的
堆在JVM中是一个可扩展的内存池,它可以根据需要动态增长或缩小,但是堆的大小也受到一定的限制

在堆中创建对象时,虚拟机会根据对象的类型和大小分配一块连续的内存空间。如果内存空间不足,即使是最简单的对象也可能抛出 OutOfMemoryError 异常。堆中的内存空间可以通过 JVM 参数进行设置,例如 -Xmx-Xms 参数可以分别设置堆的最大和初始大小

2️⃣ Java虚拟机的堆分为新生代和老年代两部分

  • 新生代是存放新建立的对象的区域

    • 它主要由一个较大的Eden(伊甸园)空间和两个较小的Survivor(幸存者)空间组成
    • 如果某个对象存活时间比较长,就会被移动到老年代中
  • 老年代是存放较旧的对象的区域,它的内存空间比新生代要大

    • 同时老年代的垃圾回收也比较少,因此需要较多的内存空间来存储
      在这里插入图片描述

堆还有其他一些内部结构,例如线程私有的本地分配缓冲区、永久代或元数据空间(在JDK 8之后,被元数据空间(Metaspace)所替代) 【Java8以后永久存储区改名为元空间】

  • 堆的大小和使用情况可以通过许多工具进行监测和优化,例如 jstat、jmap、jconsole
  • GC垃圾回收主要在新生区和养老区 【轻GC、重GC

12、新生区、老年区

Java虚拟机(JVM)主要区分为三个代:新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation,JDK8及以后被Metaspace所替代),其中新生代和老年代可以影响JVM的性能和垃圾回收机制

新生区是JVM划分的内存区域之一,主要用于存储新创建的对象

  • 新生区又分为Eden区(伊 园区)和两个Survivor区(通常为S0和S1)(幸存者区),默认比例为8:1:1
    • 当一个对象被创建时,它会被分配到Eden
    • Eden区满了时,JVM将执行Minor GC(新生代收集),清除不再使用的对象并将现有对象移动到一个 Survivor
    • 在移动过程中,对象年龄加1,也就是经过Minor GC后还存活且在Survivor区中的对象,年龄会增加
    • 当一个对象在Survivor区中的年龄达到一定的阈值(例如15岁),它会被移动到老年区

在这里插入图片描述

  • 老年区主要用于存储生命周期长、经常使用的对象,例如全局缓存或应用中的静态资源
    • 因为老年区的内存分配比较稳定,所以JVM对其垃圾回收的处理非常慢
    • 但是在一定情况下需要执行Full GC(完全垃圾回收),清除不再使用的对象,以保证JVM的垃圾收集器能够及时回收内存

总的来说,新生区和老年区的划分和管理是JVM内存管理和垃圾回收的核心机制之一,也是JVM性能和应用稳定性的重要因素之一

13、永久区

Java虚拟机(JVM)的永久区(Permanent Generation,也称元数据区Metadata Space)是一个特殊的内存区域,主要用于存储JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等内容

JVM在启动时会预设大小和最大值,初始大小和最大值可以通过命令行参数进行设置 【这个区域不存在垃圾回收,关闭VM虚拟机就会释放这个区域的内存】

  • jdk1.6 之前:永久代,常量池在方法区
  • jdk 1.7 : 永久代,但是慢慢的退化了,去永久代,常量池在堆中
  • jdk 1.8 之后:无永久代,常量池在元空间

14、堆内存调优

以下是一些常用的堆内存调优技巧:

  • 设置堆的大小:堆的大小可以通过JVM参数进行设置,例如 -Xmx-Xms 参数分别设置最大堆和初始堆的大小。适当设置堆的大小,可以避免OOM(Out of Memory)异常。
  • 对象池:可重用的Java对象可以放入对象池中,而不是每次创建新对象时使用新的内存空间。对象池可以提高性能和减少GC时间。
  • Dump文件:Dump文件可以记录当前JVM中的对象和内存使用情况,有助于诊断和解决内存泄漏或性能问题。
  • 垃圾回收器:Java虚拟机有许多垃圾回收器可供选择,例如CMS、G1、Serial等,每个垃圾回收器都有不同的优点和适用场景。可以根据应用程序的特点选择合适的垃圾回收器,并根据JVM日志进行优化。
  • 内存分析工具:内存分析工具可以帮助发现内存泄漏和优化内存使用。例如,Eclipse Memory Analyzer(MAT)是一个Java堆-Dump分析器和计算机内存调试工具,可以快速分析Dump文件。【还可以使用JProfiler
  • 避免创建过多的临时对象:在循环和递归中避免使用过多的临时对象,可以提高Java程序的性能

堆内存调优是一项复杂的任务,需要我们仔细分析应用程序的特点,了解JVM的特性和限制,以及通过不断实践和调整来改进应用程序的性能和稳定性。

15、GC

1️⃣ 什么是垃圾回收技术?

GC (Garbage Collection) 是指垃圾回收技术,是现代编程语言中的一个重要特性。垃圾回收是一种自动的内存管理技术,用于自动回收不再使用的内存,使得程序员可以更加专注于算法和业务逻辑的实现,而不必过于担心内存泄漏和关注内存管理方面的细节

在一个计算机程序中,内存是分配给程序使用的资源之一。程序会创建新的对象(实例),并使用它们执行其任务。在经过一段时间后,这些对象可能会变成无用对象(即垃圾),如未及时的回收,它们将占用宝贵的内存资源,导致系统变慢甚至崩溃

因此,垃圾回收技术是一种很重要的技术,它提供了一种自动内存管理机制,通过找出和清除不再被使用的(即垃圾)对象,以释放出内存。

2️⃣ 常见的垃圾回收机制

  • 引用计数是一种简单的垃圾回收算法
    • 跟踪每个对象的被引用次数,当一个对象的引用计数为0时,就将其释放
    • 但它存在循环引用问题,即两个或多个对象互相强引用,导致它们的引用计数永远不为0,从而无法释放

在这里插入图片描述

  • 标记-清除是一种使用标记的垃圾回收算法
    • 它使用标记-清除两个阶段
    • 标记阶段会遍历整个堆(heap)并标记所有存活的对象,而清除阶段会清理所有未被标记的对象
    • 这种方法可以解决循环引用的问题,但会产生内存碎片问题

在这里插入图片描述

  • 复制算法是一种常见的垃圾回收算法
    • 它将堆分为两个区域,每次只使用其中一个区域,当一个区域的对象占满之后,就将标记的存活对象复制到另一个区域中,并清理掉原来的区域
    • 这种算法可以保证堆中的内存连续,但空间利用率比较低

在这里插入图片描述

  • 标记-整理算法则是在标记-清除算法的基础上改进而来的
    • 它在清除未标记对象时,保证存活对象在内存中连续排列,从而减少了内存碎片问题

在这里插入图片描述

综上所述,不同类型的垃圾回收算法适用于不同的场景和应用。在开发软件时,程序员需要考虑编程语言采用什么样的垃圾回收算法以及如何最优化地使用内存。

3️⃣ 总结:

  • 内存效率:复制算法 > 标记清除算法 > 标记压缩算法(时间效率)
  • 内存整齐度:复制算法 = 标记压缩算法 > 标记清除算法
  • 内存利用率:标记压缩算法 = 标记清除算法 > 复制算法

16、JMM

JMM(Java内存模型)是描述Java程序中多线程并发访问内存的规范。Java程序在运行时会创建线程,这些线程会在共享内存中进行操作,并且可以访问同一个对象。JMM定义了Java程序中多线程之间内存访问的规则,以保证多线程并发访问内存时的正确性和可靠性

在这里插入图片描述

JMM主要定义了以下内容:

  • 内存模型:
    • JMM使用内存模型来描述Java程序中多线程并发访问共享内存的行为
    • 在内存模型中,内存会被划分为工作内存和主内存,线程只能直接访问工作内存,而不能直接操作主内存
    • 所有的操作都必须通过主内存来完成。
  • 原子性、可见性和有序性:
    • JMMJava中的共享变量提供了原子性、可见性和有序性的保证
    • 原子性指的是对于基本数据类型的操作是具有原子性的,例如int类型的自增操作是原子性的
    • 可见性指的是一个线程修改了变量的值,其他线程能够立即看到这个变化
    • 有序性指的是JMM保证程序执行的指令顺序与Java代码编写的顺序相同,如果要保证顺序,需要使用happens-before关系来确保顺序性
  • happens-before关系:
    • happens-before关系定义了两个操作之间的顺序关系
    • 如果操作A happens-before操作B,那么操作B看到操作A的结果
    • happens-before关系是JMM的一个重要概念,它可以用来确保Java程序中的指令执行顺序

总之,JMM是Java多线程编程的基础,了解JMM对于编写高质量的多线程程序是非常重要的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM入门必备 的相关文章

  • Java Try Catch Final 没有 Catch 的情况下会阻塞

    我正在审查一些新代码 该程序只有一个 try 和一个 finally 块 既然排除了 catch 块 那么如果 try 块遇到异常或任何可抛出的内容 它如何工作 它直接进入finally块吗 如果 try 块中的任何代码可以引发已检查异常
  • 主线程如何在该线程之前运行?

    我有以下代码 public class Derived implements Runnable private int num public synchronized void setA int num try Thread sleep 1
  • 记录骆驼路线

    我的项目中有几个 Camel 上下文 如果可能的话 我想以逆向工程方式记录路线 因为我们希望保持与上下文相关的文档最新 最好的方法是什么 我们倾向于预先实际设计路线 并使用来自EIP book http www eaipatterns co
  • 如何获取 WebElement 的父级[重复]

    这个问题在这里已经有答案了 我试过了 private WebElement getParent final WebElement webElement return webElement findElement By xpath 但我得到
  • 列表应该如何转换为具体的实现?

    假设我正在使用一个我不知道源代码的库 它有一个返回列表的方法 如下所示 public List
  • 获取给定类文件的目录路径

    我遇到的代码尝试从类本身的 class 文件所在的同一目录中读取一些配置文件 File configFiles new File this getClass getResource getPath listFiles new Filenam
  • Java - 返回值是否会中断循环?

    我正在编写一些基本上遵循以下格式的代码 public static boolean isIncluded E element Node
  • Android Studio 将音乐文件读取为文本文件,如何恢复它?

    gameAlert mp3是我的声音文件 运行应用程序时 它询问我该文件不与任何文件类型关联 请定义关联 我选择TextFile错误地 现在我的音乐文件被读取为文本文件 我如何将其转换回music file protected void o
  • Espresso 和 Proguard 的 Java.lang.NoClassDefFoundError

    我对 Espresso 不太有经验 但我终于成功地运行了它 我有一个应用程序需要通过 Proguard 缩小才能处于 56K 方法之下 该应用程序以 3 秒的动画开始 因此我需要等到该动画结束才能继续 这就是我尝试用该方法做的事情waitF
  • 如何在 Eclipse Java 动态 Web 项目中使用 .properties 文件?

    我正在 Eclipse 中开发动态 Web 项目 我创建了一个 properties 文件来存储数据库详细信息 用户名 密码等 我通过右键单击项目和 New gt File 添加它 我使用了Java util包Properties类 但它不
  • 逃离的正确方法是什么?使用 Oracle 12c MATCH_RECOGNIZE 时 JDBCPreparedStatement 中的字符?

    以下查询在 Oracle 12c 中是正确的 SELECT FROM dual MATCH RECOGNIZE MEASURES a dummy AS dummy PATTERN a DEFINE a AS 1 1 但它不能通过 JDBC
  • 解析输入,除了 System.in.read() 之外不使用任何东西

    我很难找到具体的细节System in read 有效 也许有人可以帮助我 似乎扫描仪会更好 但我不允许使用它 我被分配了一个任务 我应该以 Boolean Operator Boolean 的形式读取控制台用户输入 例如T F 或 T T
  • 如何通过 Inno Setup for NetBeans 使用自定义 .iss 文件

    我将 Inno Setup 5 与 NetBeans 8 一起使用 并且我已经能够创建一个安装程序来安装该应用程序C users username local appname 但是我希望将其安装在C Programfiles 我如何在 Ne
  • JVM:是否可以操作帧堆栈?

    假设我需要执行N同一线程中的任务 这些任务有时可能需要来自外部存储的一些值 我事先不知道哪个任务可能需要这样的值以及何时 获取速度要快得多M价值观是一次性的而不是相同的M值在M查询外部存储 注意我不能指望任务本身进行合作 它们只不过是 ja
  • 如何在Java中正确删除数组[重复]

    这个问题在这里已经有答案了 我刚接触 Java 4 天 从我搜索过的教程来看 讲师们花费了大量精力来解释如何分配二维数组 例如 如下所示 Foo fooArray new Foo 2 3 但我还没有找到任何解释如何删除它们的信息 从内存的情
  • JSON 到 hashmap (杰克逊)

    我想将 JSON 转换为 HashMapJackson http jackson codehaus org 这是我的 JSON String json Opleidingen name Bijz trajecten zorg en welz
  • Java的-XX:+UseMembar参数是什么

    我在各种地方 论坛等 看到这个参数 并且常见的答案是它有助于高并发服务器 尽管如此 我还是找不到 sun 的官方文档来解释它的作用 另外 它是Java 6中添加的还是Java 5中存在的 顺便说一句 许多热点虚拟机参数的好地方是这一页 ht
  • Java:多线程内的 XA 事务传播

    我如何使用事务管理器 例如Bitronix http docs codehaus org display BTM Home JBoss TS http www jboss org jbosstm or Atomikos http www a
  • 在android中跟踪FTP上传数据?

    我有一个运行 Android 的 FTP 系统 但我希望能够在上传时跟踪字节 这样我就可以在上传过程中更新进度条 安卓可以实现这个功能吗 现在 我正在使用org apache common net ftp我正在使用的代码如下 另外 我在 A
  • 在哪里存储 Java 的 .properties 文件?

    The Java教程 http download oracle com javase tutorial essential environment properties htmlon using Properties 讨论如何使用 Prop

随机推荐

  • vue3 mitt路由跳转后 on事件获取不到值的奇葩问题解决

    vue3不再支持大家试用this 那原型链这种东西自然是要命了 好在我们还有第三方插件mitt 但这东西是真的坑啊 比如我们定义 EventBus emit 然后马上进行路由跳转 EventBus emit datas Acquis rou
  • 苹果手表测心电的原理

    苹果手表 Apple Watch 测心电的原理是通过一种称为光电容积脉搏波法 photoplethysmography PPG 的技术来实现的 此外 它还使用了一种名为电极传感器的功能来检测心电图 ECG 信号 以下是苹果手表测量心电和心率
  • 加速AndroidStudio的编译和卡顿等待说拜拜!

    Android studio 2 2 当中有一项新的功能 Dex In Process 这项功能可以动态的加快编译速度 以及提高Instant Run 的效率 那么怎么来使用这项新功能呢 你只需要修改 gradle properties 这
  • Android 进程保活招式大全

    https mp weixin qq com s biz MzA3NTYzODYzMg mid 2653577617 idx 1 sn 623256a2ff94641036a6c9eea17baab8
  • Android开发网上的一些重要知识点

    1 android单实例运行方法 我们都知道Android平台没有任务管理器 而内部App维护 者一个Activity history stack来实现窗口显示和销毁 对于常规从快捷方式运行来看都是startActivity可能会使用FLA
  • SonarQube 04 SonarScanner的使用 Web Go项目扫描

    Web前端项目扫描 root jenkins master devops web service master ls build index html Jenkinsfile1 package lock json src config Je
  • Graph-node:创建一个新的subgraph

    Graph node 创建一个新的subgraph 1 合约源码 以TetherToken为例 TetherToken 2 开发子图 作为子图开发人员 您可以定义 The Graph 正在索引哪些区块链数据以及如何存储这些数据 以下是子图定
  • 为什么很多python文件中 都有这句代码if __name__ == ‘__main__‘:

    最近学习python 看到大多数写的好一点的python脚本或者程序中都会有 if name main 这句代码 然后收集了一些资料分享 1 这段代码是干嘛用的 python文件一般有两种使用方法 第一种是直接运行python文件 第二种是
  • 毕业设计基于OpenMV的火灾检测及人员搜寻智能车

    0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕设题目缺少创新和亮点 往往达不到毕业答辩的要求 这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求 为了大家能够顺利以及最少的精力通过毕设 学长分享优质毕业设计项
  • GPT-4震撼发布:多模态大模型:Plus用户优先试用

    OpenAI 刚刚宣布正式推出 GPT 4 GPT 4 是 Generative Pre trained Transformer 4 的缩写 即生成型预训练变换模型 4 这是 OpenAI 努力扩展深度学习的最新里程碑 GPT 4 是一个大
  • Linux-0.11操作系统实验5-信号量的实现和应用

    实验环境 信号量的实现和应用 实验理论 Linux 0 11操作系统实验5理论 信号量与临界区 实验任务 在 Ubuntu 下编写程序 用信号量解决生产者 消费者问题 在 linux 0 11 中实现信号量 用生产者 消费者程序检验之 用信
  • python 快速排序的实现

    快速排序 快速排序 Quicksort 是对冒泡排序的一种改进 快速排序算法通过多次比较和交换来实现排序 其排序流程如下 首先设定一个分界值 通过该分界值将数组分成左右两部分 将大于或等于分界值的数据集中到数组右边 小于分界值的数据集中到数
  • DVWA-10.XSS (DOM)

    大约 跨站点脚本 XSS 攻击是一种注入问题 其中恶意脚本被注入到原本良性和受信任的网站上 当攻击者使用 Web 应用程序发送恶意代码 通常以浏览器端脚本的形式 时 就会发生 XSS 攻击 给其他最终用户 允许这些攻击成功的缺陷非常普遍 并
  • IDEA2021/2020 Run Dashboard的打开方法

    IDEA中微服务在service窗口以配置方式启动 网上千篇一律的在 workspace xml中配置Run Dashboard 但是只有在旧版本中才有这个节点的配置 但是新版的该怎么办呢 压根就没有这个节点 硬着头皮添加进去你会发现 问题
  • 卫星电话是直接与卫星通信还是通过地面站?

    问题 卫星电话是直接与卫星通信还是通过地面站 如果直接与卫星通信 那么得多大的发射功率 那么小的手机怎么能达到那个功率 如果是通过地面站的话 那么地面站岂不是和现在的蜂窝小区基站差不多了吗 我查卫星通信的时候 有一个人举例说明卫星通信的作用
  • 病毒分类

    根据病毒存在 隐藏 感染和激活的方式进行分类 寄生病毒 这是最常见的病毒类型 病毒并不是一个独立的程序 而是嵌入某个实用程序的一段代码 嵌入病毒的实用程序 称为感染了病毒的实用程序 一旦运行感染了病毒的实用程序 将首先激活病毒 由病毒完成对
  • 一、MicroPython移植到任意平台(简介以及源码裁剪)

    目录 一 MicroPython简介 二 MicroPython源码 1 源码获取 2 源码介绍 三 移植条件 四 系统结构 五 移植过程 一 MicroPython简介 适用于嵌入式设备的python开源解释器 占用资源少 可移植性强 二
  • linux下shell脚本启动jar包

    样例1 bin bash jar包文件路径及名称 目录按照各自配置 APP NAME home jar sys modules gen lgs modules gen jar 日志文件路径及名称 目录按照各自配置 LOG FILE home
  • C++函数模板和类模板的声明和定义问题

    C 函数模板和类模板的声明和定义问题 编译器并不是把函数模板处理成能够处理任意类的函数 编译器从函数模板通过具体类型产生不同的函数 编译器会对函数模板进行两次编译 在声明的地方对模板代码本身进行编译 在调用的地方对参数替换后的代码进行编译
  • JVM入门必备

    1 JVM 的位置 2 JVM 的体系结构 JVM Java虚拟机 是Java程序的运行环境 它对于Java平台的运行和跨平台特性的实现有着重要的作用 JVM的体系结构有以下几个部分 类加载器 ClassLoader 负责将 class字节