Java并发+JVM

JMM

JMM(Java Memory Model,Java 内存模型)是一种规范,用于描述 Java 程序中多线程并发访问共享内存时的行为。JMM 定义了程序中各种变量(包括实例字段、静态字段和构成数组对象的元素)的访问方式,以及程序执行期间内存的分配、读写和同步操作等行为规范。

JMM 主要包括以下几个方面的内容:

1. **主内存和工作内存:** JMM 规定了每个线程都有自己的工作内存,用于存储线程需要访问的变量副本。所有线程共享的内存称为主内存。线程的工作内存和主内存之间通过读写操作进行数据同步。

2. **内存屏障:** JMM 规定了一些特殊的指令,称为内存屏障(Memory Barrier),用于控制主内存和工作内存之间的数据同步,包括读屏障、写屏障和全屏障。

3. **原子性:** JMM 保证了对于基本数据类型的读取和赋值操作是原子性的,但对于非原子性操作(如 long 和 double 类型的赋值、递增和递减等),需要通过锁或者 volatile 等机制来保证原子性。

4. **可见性:** JMM 保证了一个线程对于某个变量的修改,对于其他线程是可见的,但不保证立即可见,需要通过同步机制来确保变量的可见性。

5. **有序性:** JMM 规定了在一个线程中,程序的执行顺序必须与代码的书写顺序一致;而在不同线程之间,并没有明确的执行顺序保证,需要通过同步机制来保证执行的有序性。

总的来说,JMM 提供了一套规范,使得开发人员能够更好地编写多线程程序,确保程序在并发访问共享内存时能够正确、高效地运行。

构造方法可以用 synchronized 修饰么?

先说结论:构造方法不能使用 synchronized 关键字修饰。

构造方法本身就属于线程安全的,不存在同步的构造方法一说。

构造方法被认为是线程安全的主要是因为在多线程环境下,当一个线程执行对象的构造方法时,其他线程无法同时访问该对象,因为对象还没有完全构造完成,不会被其他线程所见。换句话说,对象的构造过程是一个原子操作,要么完全完成,要么不执行。

对象的访问定位的两种⽅式知道吗?各有什么优缺点

对象的访问定位通常有两种方式:直接指针访问句柄访问

1. **直接指针访问**:
   - **优点**:直接指针访问方式将对象的实例数据对象头存储在连续的内存中,因此可以通过对象的内存地址直接访问对象数据,访问速度较快
   - **缺点**:由于对象的实例数据和对象头存储在连续的内存中,当对象被移动时,所有指向该对象的指针都需要进行更新,这样会增加对象移动的开销,并且在进行垃圾回收时需要考虑对象的移动问题。

2. **句柄访问**:
   - **优点**:句柄访问方式将对象的实例数据和对象头分别存储在堆内存和堆栈内存中,通过堆栈内存中的句柄引用对象,在堆内存中可以动态地移动对象而不影响堆栈内存中的句柄,从而避免了直接指针访问方式中对象移动的开销
   - **缺点**:由于句柄访问方式需要通过堆栈内存中的句柄来间接引用对象,因此会增加一次额外的内存访问开销,导致访问速度稍慢一些

总的来说,直接指针访问方式在访问速度上更快,但在对象移动时会带来额外的开销;而句柄访问方式可以更灵活地管理对象的内存,但访问速度稍慢一些。在实际应用中,选择哪种访问方式取决于应用的具体需求和性能要求。

如何判断对象是否死亡(两种⽅法)。 讲⼀下可达性分析算法的流程。

1、引用计数法

在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器就减1;任何时刻计数器为0的对象就是不可能再被使用的。

2.可达性分析算法

这个算法基于的思想是,通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,能够被搜索到的对象就被认为是存活的,不能被搜索到的对象就被判定为死亡,需要被回收。

可达性分析算法的流程如下:

1. **GC Roots**:作为起始点的对象集合。通常包括:
   - 虚拟机栈中引用的对象(本地变量表)
   - 方法区中类静态属性引用的对象
   - 方法区中常量引用的对象
   - 本地方法栈中JNI(即native方法)引用的对象

2. **搜索与标记**:从GC Roots开始,沿着对象的引用关系进行遍历和搜索,标记所有被引用到的对象为存活状态。

3. **清除未标记对象**:在完成标记阶段后,所有未被标记的对象将被判定为死亡,即不再被任何存活对象引用,需要被回收。

4. **回收**:对死亡对象进行垃圾回收,释放它们所占用的内存空间。

JDK 中有⼏种引⽤类型?分别的特点是什么?

在Java中,常见的引用类型有四种:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。它们各自具有不同的特点和用途。

1. **强引用(Strong Reference)**:
   - 特点:最常见的引用类型,使用`new`关键字创建的对象默认就是强引用。
   - 特点:只要强引用存在,垃圾回收器就不会回收被引用的对象。
   - 用途:通常用于实现普通对象的引用,是默认的引用类型。

2. **软引用(Soft Reference)**:
   - 特点:用`SoftReference`类创建的对象,当内存不足时,垃圾回收器会尝试回收这些对象,但不是强制性的,只有在内存不足时才会回收。
   - 特点:主要用于实现内存敏感的缓存,允许在内存不足时释放一些缓存对象,防止内存溢出。

3. **弱引用(Weak Reference)**:
   - 特点:用`WeakReference`类创建的对象,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。
   - 特点:当垃圾回收器工作时,无论当前内存是否充足,都会回收被弱引用关联的对象。
   - 用途:主要用于实现对象的缓存,但比软引用更“弱”,在下一次垃圾回收时会更积极地回收对象。

4. **虚引用(Phantom Reference)**:
   - 特点:用`PhantomReference`类创建的对象,虚引用的对象在任何时候都可能被垃圾回收器回收。
   - 特点:主要用于实现比较精细的引用队列和跟踪对象被垃圾回收的状态,但无法通过虚引用直接取得被引用的对象。
   - 用途:在对象被回收前,会收到一个系统通知,可以在这个时机做一些后续处理,例如清理一些资源。

总的来说,不同的引用类型适用于不同的场景和需求,能够帮助我们更灵活地管理对象的生命周期和内存占用。

堆空间的基本结构了解吗?什么情况下对象会进⼊⽼年代?

Hotspot 遍历所有对象时,按照年龄从小到大对其所占用的大小进行累加,当累加到某个年龄时,所累加的大小超过了 Survivor 区的一半,则取这个年龄和 MaxTenuringThreshold 中更小的一个值,作为新的晋升年龄阈值

有哪些常⻅的 GC?谈谈你对 Minor GC、还有 Full GC 的理解。Minor GC 与 Full GC 分别在什么时候发 ⽣? Minor GC 会发⽣ stop the world 现象吗?

常见的GC包括:

1. Serial GC(串行GC)
2. Parallel GC(并行GC)
3. CMS GC(Concurrent Mark-Sweep GC,并发标记清除GC)
4. G1 GC(Garbage-First GC)

Minor GC(新生代GC)和Full GC(老年代GC)是针对堆内存中不同区域的垃圾回收操作。

- Minor GC:发生在新生代,主要回收年轻代的内存空间。当Eden区域填满时,触发Minor GC。在Minor GC中,会将Eden区和部分Survivor区中的存活对象复制到另一个Survivor区,同时清空Eden区和之前的Survivor区。Minor GC通常是快速的,因为大部分对象都是短时间存活的,因此只有少量对象需要被回收。Minor GC过程中会发生STW(Stop-The-World)现象,即应用程序的所有线程都会被暂停,直到GC完成。

- Full GC:发生在老年代,主要回收老年代的内存空间。Full GC会对整个堆空间进行垃圾回收,包括新生代和老年代。Full GC的触发条件通常包括老年代空间不足、永久代空间不足(如果存在永久代),或者CMS GC出现并发失败等情况。Full GC过程中也会发生STW现象,暂停所有应用线程。

Minor GC会发生STW现象,因为在复制存活对象到另一个Survivor区时,需要确保堆空间的一致性。STW的时间通常较短,但在某些情况下,例如Eden区和Survivor区中的对象数量非常大时,Minor GC的暂停时间可能会变长。

并发标记要解决什么问题?并发标记带来了什么问题?如何解决并发扫描时对象消失问题

并发标记主要解决的问题是尽量减少垃圾回收对应用程序的停顿时间,即STW(Stop-The-World)时间。在并发标记阶段,垃圾收集器会在应用程序运行的同时,通过并发扫描来标记出存活对象。这样,垃圾收集器可以尽量在后台进行标记工作,减少应用程序的停顿时间,提高系统的响应性和吞吐量。

然而,并发标记也带来了一些问题,其中最常见的问题是对象的可达性关系可能会发生变化。在并发标记期间,应用程序可能会修改对象之间的引用关系,甚至删除对象。如果在并发扫描的过程中,某个对象被标记为存活对象,但是在标记完成之后,该对象的引用被删除,那么这个对象就会被错误地保留下来,从而造成内存泄漏或者垃圾回收器无法释放该对象所占用的内存空间。

为了解决这个问题,垃圾回收器通常会在标记阶段和清除阶段之间留出一段缓冲时间,称为安全点(Safe Point)。在安全点,应用程序的执行会被暂停,垃圾回收器会重新扫描对象,并且检查对象的引用关系是否发生了变化。如果发现有对象的引用关系发生了变化,垃圾回收器会重新标记并清除这些对象。通过这种方式,可以保证在并发标记过程中对象的可达性关系是正确的,避免了因为并发扫描时对象消失而导致的问题。

G1 垃圾收集器的步骤。有什么缺点

初始标记-并发标记-最终标记-筛选回收

停顿时间长,响应速度慢。

JVM 中的安全点和安全区各代表什么?写屏障你了解吗?

深入学习JVM-JVM 安全点和安全区域 - 李东平|一线码农 - 博客园 (cnblogs.com)

什么是字节码?类⽂件结构的组成了解吗?

字节码是一种中间代码,它是Java源代码编译后的结果,也是Java虚拟机(JVM)可执行的指令集。Java源代码经过编译器编译后生成字节码文件,通常以.class为扩展名,其中包含了Java程序的执行指令、常量池、方法信息等。

类文件结构包括以下几个主要部分:

1. **魔数(Magic Number):** 类文件的前4个字节称为魔数,它的值固定为0xCAFEBABE,用于识别文件是否是一个有效的Java类文件。

2. **版本信息(Version):** 类文件的接下来的4个字节包含了两个无符号整数,分别表示类文件的主版本号和次版本号。

3. **常量池(Constant Pool):** 常量池是类文件中的一个重要部分,包含了类文件中使用的各种字面量和符号引用。常量池中的内容包括类名、字段名、方法名、字符串字面量等,它们被用于构建类的结构,以及在类的加载、解析和运行时的动态链接中使用。

4. **访问标志(Access Flags):** 类的访问标志描述了类的访问级别和属性,包括public、private、protected、final等。

5. **类信息(Class Information):** 类信息包括类的修饰符、父类、接口等信息。

6. **字段表(Field Table):** 字段表描述了类的字段信息,包括字段的修饰符、名称、类型等。

7. **方法表(Method Table):** 方法表描述了类的方法信息,包括方法的修饰符、名称、参数列表、返回类型等。

8. **属性表(Attribute Table):** 属性表包含了类、字段、方法等各个部分的附加信息,比如源文件名、行号信息、注解等。

以上是Java类文件的基本结构,它们组合在一起构成了Java类的定义和结构信息,Java虚拟机通过解析类文件来加载类、执行方法等操作。

加载这⼀步主要做了什么事情?初始化阶段中哪⼏种情况必须对类初始化?

  1. 通过全类名获取定义此类的二进制字节流。
  2. 将字节流所代表的静态存储结构转换为方法区的运行时数据结构。
  3. 在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口。

对于类的初始化阶段,有几种情况是必须对类进行初始化的:

  1. 创建类的实例:当通过 new 关键字创建类的实例时,必须对类进行初始化。这包括直接创建类的实例和通过反射创建类的实例。

  2. 访问类的静态变量或静态方法:如果在程序中访问了类的静态变量或静态方法,而且这些变量或方法没有在编译器阶段就被赋值,那么就需要对类进行初始化。

  3. 使用java.lang.reflect包进行反射调用:如果通过反射机制调用类的方法或字段,并且该类尚未被初始化,那么在调用前会触发类的初始化。

双亲委派模型了解么?如果我们不想⽤双亲委派模型怎么办?

双亲委派模型是 Java 类加载机制中的一种设计思想。根据这个模型,当一个类加载器接收到加载类的请求时,它首先会委托给父类加载器加载,只有在父类加载器无法加载该类时,才由当前类加载器自己尝试加载。这种层层向上委派的机制保证了类的加载顺序,防止同一个类被多次加载,确保了类的唯一性和一致性。

如果不想使用双亲委派模型,主要有两种方法:

重写ClassLoader的loadClass方法:在自定义ClassLoader时,可以重写其loadClass方法,自定义加载类的逻辑,不遵循双亲委派模型。但是这种做法需要特别小心,因为破坏了类加载器的层级关系,可能导致类的重复加载或冲突。

(如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。但是,如果想打破双亲委派模型则需要重写 loadClass() 方法)

双亲委派模型有什么好处?

双亲委派模型保证了 Java 程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了 Java 的核心 API 不被篡改。

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值