JVM 学习

这里写目录标题

  • 说一说JVM的内存区域
  • JVM 内存模型
  • volatile的使用及其原理
  • Java中类加载过程是什么样的?
  • 方法区内存溢出怎么处理?
  • 谈谈动态年龄判断
  • 哪些是 GC Roots?
  • 强引用、软引用、弱引用、虚引用是什么,有什么区别?
  • 工作中常用的 JVM 配置参数有哪些?
  • 谈谈对 OOM 的认识
  • 谈谈你知道的垃圾回收算法
  • JIT 是什么?
  • 谈谈双亲委派模型 列举一些你知道的打破双亲委派机制的例子。为什么要打破?
  • 说一下垃圾分代收集的过程
  • 如何找到死锁的线程?
  • 描述一下什么情况下,对象会从年轻代进入老年代
  • safepoint 是什么?
  • MinorGC、MajorGC、FullGC 什么时候发生?
  • 生产环境 CPU 占用过高,你如何解决?
  • 生产环境服务器变慢,如何诊断处理?

说一说JVM的内存区域

Java虚拟机(JVM)的内存可以划分为以下几个区域,每个区域有不同的作用和用途:

  1. 程序计数器(Program Counter Register):
    程序计数器是JVM中的一块较小的内存区域,它是当前线程所执行的字节码指令的行号指示器。在多线程环境下,每个线程都有一个独立的程序计数器,保证线程切换后能够恢复到正确的执行位置。由于程序计数器只记录线程执行的位置,并不存储其他信息,所以它是线程私有的。

  2. Java虚拟机栈(Java Virtual Machine Stacks):
    Java虚拟机栈也称为Java栈,用于存储Java方法的局部变量、操作数栈、动态链接和方法返回地址等。每个方法执行时都会在栈上创建一个栈帧,栈帧包含了该方法的局部变量和操作数栈等信息。栈帧随着方法的调用和返回而出栈和入栈,是线程私有的。

  3. 本地方法栈(Native Method Stack):
    本地方法栈类似于Java虚拟机栈,但它为本地方法(Native Method)服务。本地方法是用C、C++等语言编写的方法,它们与Java代码进行交互。本地方法栈也是线程私有的。

  4. Java堆(Java Heap):
    Java堆是Java虚拟机所管理的内存中最大的一块,用于存储对象实例和数组。在Java堆中创建的对象可以被所有线程访问。堆内存在JVM启动时就被分配,所有的对象实例都在堆上进行分配和回收。Java堆是垃圾回收器的主要工作区域,垃圾回收器通过对Java堆进行垃圾回收来回收不再使用的对象。

  5. 方法区(Method Area):
    方法区用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区是所有线程共享的,它在JVM启动时被分配,并且随着时间的推移动态扩展。Java 8及之前版本中,方法区是永久代(Permanent Generation)的实现;而Java 8之后,永久代被元空间(Metaspace)所取代,元空间位于本地内存中,不再在堆中。

  6. 运行时常量池(Runtime Constant Pool):
    运行时常量池是方法区的一部分,用于存储编译器生成的常量以及运行时产生的常量。它包含了类和接口中的常量、字符串字面量和符号引用等信息。

除了以上几个主要的内存区域,JVM还可以划分出一些特殊的内存区域,如直接内存(Direct Memory)。直接内存并不是JVM管理的,而是通过Java NIO库与本地代码直接交互的。

JVM 内存模型

Java虚拟机规范中定义的"内存模型",它是为了解决多线程并发访问共享内存时可能出现的可见性、有序性和原子性等问题而设计的。

Java虚拟机内存模型规定了所有线程共享的主内存和每个线程私有的工作内存之间的关系和操作。主内存是所有线程共享的内存区域,用于存储Java对象实例和类的数据。每个线程都有自己的工作内存,用于存储主内存中的共享变量的副本。

Java虚拟机内存模型通过一些规则来保证多线程并发访问共享内存时的正确性和一致性:

  1. 原子性(Atomicity): 内存操作是原子的,不可被中断,要么全部执行成功,要么全部不执行。

  2. 可见性(Visibility): 当一个线程修改了共享变量的值,其他线程能够立即看到这个修改。

  3. 有序性(Ordering): 内存操作会按照代码的顺序执行,但在不影响单线程执行结果的前提下,可能会被重排序。

  4. volatile关键字: 使用volatile关键字可以保证共享变量的可见性和有序性,但不能保证原子性。

  5. synchronized关键字: 使用synchronized关键字可以保证共享变量的原子性、可见性和有序性,但可能会引起线程的阻塞和唤醒。

  6. 锁机制: Java提供了锁机制,如ReentrantLock、ReadWriteLock等,用于保护共享资源,实现对共享变量的互斥访问。

Java虚拟机内存模型规定了这些操作的具体语义,以及volatile和synchronized等关键字的语义。这样,开发者可以通过使用合适的同步机制来确保多线程程序的正确性,并充分利用硬件和操作系统的特性,实现高效的并发编程。Java虚拟机内存模型在多线程编程中起到了重要的规范作用,确保了多线程程序的正确性和可靠性。

volatile的使用及其原理

volatile是Java关键字之一,用于修饰变量,主要用于保证变量的可见性和禁止指令重排序。使用volatile关键字修饰的变量,对于多线程环境中的读写操作,会有以下特性:

  1. 可见性(Visibility): 当一个线程修改了一个volatile变量的值,其他线程可以立即看到这个修改,即保证了可见性。这是因为volatile变量会直接从主内存中读取最新的值,而不是使用线程的本地缓存。

  2. 禁止指令重排序: 在Java虚拟机中,为了提高性能,会对指令进行重排序。但对volatile变量的读写操作会在指令重排序时添加特殊的内存屏障,使得变量的读写操作不会被重排序,保证了操作的有序性。

volatile的原理是使用内存屏障(Memory Barrier)来实现可见性和禁止指令重排序。内存屏障是一种CPU指令,它能够强制刷新处理器缓存,保证处理器缓存中的数据与主内存中的数据一致。在读取volatile变量时,会插入读屏障,确保读取的是最新的值。在写入volatile变量时,会插入写屏障,确保写入的值对其他线程可见。读屏障和写屏障都会阻止指令重排序,保证操作的有序性。

需要注意的是,虽然volatile能够保证可见性和有序性,但不能保证原子性。多个线程同时对volatile变量进行写操作可能会导致写操作的覆盖,因此如果需要保证原子性,仍然需要使用其他同步机制,如synchronized关键字或java.util.concurrent.atomic包中的原子类。

volatile关键字适用于以下场景:

  • 一个变量被多个线程共享,并且其中一个线程对变量的修改需要立即对其他线程可见。
  • 对变量的写操作不依赖于当前值,或者能够确保只有单线程对变量进行写操作。

总结起来,volatile关键字是一种轻量级的同步机制,用于保证可见性和有序性,并且适用于特定的多线程编程场景。

Java中类加载过程是什么样的?

Java中的类加载过程是将Java字节码文件(.class文件)加载到内存中,并在Java虚拟机中创建一个Class对象,表示该类的结构和信息。类加载过程可以分为以下几个步骤:

  1. 加载(Loading):
    类加载的第一个阶段是加载,它是指将类的字节码文件从磁盘读取到内存中,并创建一个对应的Class对象。在这个阶段,会对字节码进行校验,以确保字节码的格式正确并符合Java虚拟机规范。

  2. 连接(Linking):
    连接阶段又分为三个步骤:验证(Verification)、准备(Preparation)和解析(Resolution)。

    • 验证:对加载的字节码进行验证,确保它满足Java虚拟机规范和安全要求。
    • 准备:为类的静态变量(类变量)分配内存,并设置默认初始值。
    • 解析:将类的符号引用转换为直接引用,例如将方法的符号引用转换为实际的方法入口地址。
  3. 初始化&#

你可能感兴趣的:(给我个offer吧,java,jvm)