阿里面试:JVM 锁内存 是怎么变化的? JVM 锁的膨胀过程 ?

简介: 尼恩,一位经验丰富的40岁老架构师,通过其读者交流群分享了一系列关于JVM锁的深度解析,包括偏向锁、轻量级锁、自旋锁和重量级锁的概念、内存结构变化及锁膨胀流程。这些内容不仅帮助群内的小伙伴们顺利通过了多家一线互联网企业的面试,还整理成了《尼恩Java面试宝典》等技术资料,助力更多开发者提升技术水平,实现职业逆袭。尼恩强调,掌握这些核心知识点不仅能提高面试成功率,还能在实际工作中更好地应对高并发场景下的性能优化问题。

尼恩说在前面

在40岁老架构师 尼恩的读者交流群(50+)中,很多小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试机会,遇到很多很重要的面试题:

1.请解释 JVM 偏向锁、轻量级锁、自旋锁、重量级锁什么?

2.请介绍一下什么是sychronized的自旋锁、偏向锁、轻量级锁、重量级锁?

3.请介绍一下 jvm 内置锁 的膨胀过程?

4.请介绍一下 jvm 内置锁 的膨胀过程中锁内存怎么变化的?

5.请介绍一下 jvm 内置锁 的 从轻量级锁升级重量级锁内存怎么变化的?

6.请介绍一下 jvm 锁的 膨胀过程?锁内存怎么变化的?

最近有小伙伴在面试阿里,又遇到了 jvm 内置锁 膨胀相关的面试题。小伙伴 支支吾吾的说了几句,没说清楚,面试挂了。

所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。

当然,上面的面试题以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书

本文作者:

  • 第一作者 Moen (负责写初稿 ,资深架构师)
  • 第二作者 尼恩 (40岁老架构师, 负责提升此文的 技术高度,让大家有一种 俯视 技术的感觉)

为啥内置锁存在多种状态?

在JDK1.6版本之前,所有的Java内置锁都是重量级锁。重量级锁会造成CPU在用户态和核心态之间频繁切换,所以代价高、效率低。

JDK1.6版本为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”实现。

所以,在JDK1.6版本里内置锁一共有四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,这些状态随着竞争情况逐渐升级。

内置锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种能升级却不能降级的策略,其目的是为了提高获得锁和释放锁的效率。

为什么会存在锁升级现象?

synchronized 最初的实现方式是 “阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间。

在java5及其以前,只有synchronized 这个是重量级锁,是操作系统级别的重量级操作。

重量级锁两大问题:

  • 如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长,

  • 假如锁的竞争比较激烈,性能下降。

因为重量级锁 存在用户态和内核态之间的转换。

在这里插入图片描述

java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程,就需要操作系统介入,需要在户态与核心态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,

用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。

这也是在JDK6以前 synchronized效率低下的原因,JDK6中为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。

一图总览:锁的内存变化及膨胀流程图

在这里插入图片描述

二、锁的四种状态

这种方式就是 synchronized实现同步最初的方式,这也是当初开发者诟病的地方,

所以目前锁状态一种有四种,从级别由低到高依次是:无锁、偏向锁,轻量级锁,重量级锁,锁状态只能升级,不能降级

锁状态的思路以及特点

锁状态 存储内容 标志位
无锁 对象的hashCode、对象分代年龄、是否是偏向锁(0) 01
偏向锁 偏向线程ID、偏向时间戳、对象分代年龄、是否是偏向锁(1) 01
轻量级锁 指向栈中锁记录的指针 00
重量级锁 指向互斥量的指针 11

如图所示:
在这里插入图片描述

在32位的虚拟机中:
在这里插入图片描述

在64位的虚拟机中:
在这里插入图片描述

锁对比

优点 缺点 适用场景
偏向锁 加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距 如果线程间存在锁竞争,会带来额外的锁撤销的消耗 适用于只有一个线程访问同步块场景
轻量级锁 竞争的线程不会阻塞,提高了程序的响应速度 如果始终得不到索竞争的线程,使用自旋会消耗CPU 追求响应速度,同步块执行速度非常快
重量级锁 线程竞争不使用自旋,不会消耗CPU 线程阻塞,响应时间缓慢 追求吞吐量,同步块执行速度较慢

2.4.1 偏向锁/轻量级锁/重量级锁

总体而言,Java对象(Object实例)结构包括三部分:对象头、对象体、对齐字节。具体如图2-4所示。

在这里插入图片描述

1. Java对象(Object实例)的三个部分

(1)对象头

对象头包括三个字段,第一个字段叫做_mark Word(标记字),用于存储自身运行时的数据例如GC标志位、哈希码、锁状态等信息。

第二个字段叫做_klass Pointer(类对象指针),用于存放此对象的元数据(InstanceKlass)的地址。通过_klass 指针,虚拟机通过可以确定这个对象是哪个类的实例.

第二个字段叫做Array Length(数组长度)。如果对象是一个Java数组,那么此字段必须有,用于记录数组长度的数据;如果对象不是一个Java数组,那么此字段不存在,所以这是一个可选字段。

(2)对象体

对象体包含了对象的实例变量(成员变量)。用于成员属性值,包括父类的成员属性值。这部分内存按4字节对齐。

(3)对齐字节

对齐字节也叫做填充对齐,其作用是用来保证Java对象在所占内存字节数为8的倍数(8N bytes)。HotSpot VM的内存管理要求对象起始地址必须是8字节的整数倍。对象头本身是8的倍数,当对象的实例变量数据不是8的倍数,便需要填充数据来保证8字节的对齐。

2. 对象结构中的核心字段作用

接下来,对Object实例结构中几个重要的字段的作用做一下简要说明:

(1)_mark(标记字)字段主要用来表示对象的线程锁状态,另外还可以用来配合GC、存放该对象的hashCode。

(2)_klass(类对象指针)字段是一个指向方法区中类元数据信息的指针,意味着该对象可随时知道自己是哪个Class(实际为InstanceKlass)的实例。

(3)Array Length(数组长度)字段也占用32位(在32位JVM中)的字节,这是可选的,只有当本对象是一个数组对象时才会有这个部分。

(4)对象体用于保存对象属性值,是对象的主体部分,占用的内存空间大小取决于对象的属性数量和类型。

(5)对齐字节并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。当对象实例数据部分没有对齐(8字节的整数倍)时,就需要通过对齐填充来补全。

3. 对象结构中的字段长度

Mark Word(表_mark成员)、_klass Pointer(表_klass成员)、Array Length等字段的长度,都与JVM的位数有关。

Mark Word的长度为JVM的一个Word(字)大小,也就是说32位JVM的Mark Word为32位,64位JVM为64位。Klass Pointer(类对象指针)字段的长度也为JVM的一个Word(字)大小,即32位的JVM为32位,64位的JVM为64位。

所以,

  • 在32位JVM虚拟机中,Mark Word和Klass Pointer这两部分都是32位的;

  • 在64位JVM虚拟机中,Mark Word和Klass Pointer这两部分都是64位的。

对于对象指针而言,如果JVM中对象数量过多,使用64位的指针将浪费大量内存,通过简单统计,64位的JVM将会比32位的JVM多耗费50%的内存。

为了节约内存可以使用选项+UseCompressedOops开启指针压缩。

选项UseCompressedOops中的Oop部分为Ordinary object pointer普通对象指针的缩写。

如果开启UseCompressedOops选项,以下类型的指针将从64位压缩至32位:

2.4.2 Mark Word的结构信息

Java内置锁的涉及到很多重要信息,这些都存放在对象结构中,并且是存放于对象头的 Mark Word字段中。Mark Word的位长度为JVM的一个Word大小,也就是说32位JVM的Mark word为32位,64位JVM为64位。

Mark Word的位长度不会受到Oop对象指针压缩选项的影响。

Java内置锁的状态总共有四种,级别由低到高依次为:无锁、偏向锁、轻量级锁、重量级锁。

其实在 JDK 1.6之前,Java内置锁还是一个重量级锁,是一个效率比较低下的锁,在JDK 1.6之后,JVM为了提高锁的获取与释放效率,对synchronized的实现进行了优化,引入了偏向锁、轻量级锁的实现,从此以后Java内置锁的状态就有了四种(无锁、偏向锁、轻量级锁、重量级锁),并且四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级,也就是说只能进行锁升级(从低级别到高级别)。

不同锁状态下的Mark word字段结构

Mark word字段的结构,与Java内置锁的状态强相关。为了让Mark word字段存储更多的信息,JVM将Mark word的最低两个位设置为Java内置锁状态位,不同锁状态下的32位Mark Word结构,如表2-1所示。

在这里插入图片描述

在这里插入图片描述

尼恩提示: 以上内容比较复杂,后面会在《尼恩Java面试宝典 配套视频》中,进行详细解读。

如果没有 面试机会,可以找尼恩来帮忙,打造一个绝世好简历,实现 职业逆袭中厂大龄34岁,被裁8月收一大厂offer, 年薪65W,转架构后逆天改命!

1. 无锁状态

Java对象刚创建时,还没有任何线程来竞争,说明该对象处于无锁状态(无线程竞争它)这偏向锁标识位是0、锁状态01。

无锁状态下对象的Mark Word如图2-7所示。

在这里插入图片描述

图2-7 无锁状态对象的Mark Word

2. 偏向锁状态

偏向锁是指一段同步代码一直被同一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。如果内置锁处于偏向状态,当有一个线程来竞争锁时,先用偏向锁,表示内置锁偏爱这个线程,这个线程要执行该锁关联的同步代码时,不需要再做任何检查和切换。偏向锁在竞争不激烈的情况下,效率非常高。

偏向锁状态的Mark Word会记录内置锁自己偏爱的线程ID,内置锁会将该线程当做自己的熟人。偏向锁状态下对象的Mark Word具体如图2-8所示。

在这里插入图片描述

图2-8 偏向锁状态内置锁的Mark Word

3. 轻量级锁状态

当有两个线程开始竞争这个锁对象,情况发生变化了,不再是偏向(独占)锁了,锁会升级为轻量级锁,两个线程公平竞争,哪个线程先占有锁对象,锁对象的Mark Word就指向哪个线程的栈帧中的锁记录。轻量级锁状态下对象的Mark Word具体如图2-9所示。

在这里插入图片描述

图2-9 轻量级锁状态内置锁的Mark Word

当锁处于偏向锁的时候,而又被另一个线程所企图抢占时,偏向锁就会升级为轻量级锁。企图抢占的线程会通过自旋的形式尝试获取锁,不会阻塞抢锁线程,以便提高性能。

自旋原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。

但是线程自旋是需要消耗 CPU的,如果一直获取不到锁,那线程也不能一直占用 CPU自旋做无用功,所以需要设定一个自旋等待的最大时间。

JVM 对于自旋周期的选择,JDK1.6之后引入了适应性自旋锁,适应性自旋锁意味着自旋的时间不是固定的,而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定。线程如果自旋成功了,则下次自旋的次数会更多,如果自旋失败了,则自旋的次数就会减少。

如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,就会导致其他争用锁的线程在最大等待时间内还是获取不到锁,自旋不会一直持续下去,这时争用线程会停止自旋进入阻塞状态,该锁膨胀为重量级锁。

4. 重量级锁状态

重量级锁会让其他申请的线程之间进入阻塞,性能降低。重量级锁也就叫做同步锁,这个锁对象Mark Word再次发生变化,会指向一个监视器(Monitor)对象,该监视器对象用集合的形式,来登记和管理排队的线程。重量级锁状态下对象的Mark Word具体如图2-10所示。

在这里插入图片描述

图2-10 重量级锁状态内置锁的Mark Word

三:如何获得偏向锁

下面的一段代码,如何获得偏向锁?

 synchronized (lock)
                {
                    lock.increase();
                    if (i == MAX_TURN / 2)
                    {
                        Print.tcfo("占有锁, lock 的状态: ");
                        lock.printObjectStruct();
                    }
                }

偏向锁的核心原理是:如果不存在 竞争的一个线程获得了锁,那么锁就从无锁状态,进入偏向状态,此时,Mark Word 的结构变为偏向锁结构,

  • 锁对象的锁标志位(lock)被改为01,

  • 偏向标志位(biased_lock)被改为1,

  • 然后线程的thread ID记录在锁对象的Mark Word中(使用CAS操作完成)。

以后该线程获取锁的时,判断一下线程ID和标志位,就可以直接进入同步块,连CAS操作都不需要,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。

在这里插入图片描述

偏向锁比较极致,干脆就把同步取消掉,不需要进行CAS了。

偏向锁的发现,主要得益于人们发现某个线程可以频繁的获取到锁。

偏向锁其实就是为了单个线程设计的。

如果某个锁资源一直是被某个线程获取,而且没有其它线程来获取锁,就可以在Mark Word中记录下这个线程id,该线程就没有必要花时间来进行CAS操作了,可以直接进入到同步代码块。

直到发现有其它线程来抢占锁资源了,就会根据当前状态判断是否把偏向锁膨胀成为轻量级锁。

如果需要使用偏向锁,可以使用参数:-XX:+UseBiased参数来添加。

在JDK1.6之后是默认开启的,但是启动时间有延迟(4秒),

在JDK1.6之后,需要添加参数-XX:BiasedLockingStartupDelay=0,让其在程序启动时立刻启动

开启偏向锁:

-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

关闭偏向锁:关闭之后程序默认会直接进入------->轻量级锁状态

-XX:-UseBiasedLocking

尼恩提示: 以上内容比较复杂,后面会在《尼恩Java面试宝典 配套视频》中,进行详细解读。

如果没有 面试机会,可以找尼恩来帮忙,打造一个绝世好简历,实现 职业逆袭中厂大龄34岁,被裁8月收一大厂offer, 年薪65W,转架构后逆天改命!

四:如何膨胀到 轻量级锁

多线程竞争,但是任意时刻最多只有一个线程竞争,即不存在锁竞争太过激烈的情况,也就没有线程阻塞。

有线程来参与锁的竞争,但是获取锁的冲突时间极短。

轻量级锁本质就是自旋锁CAS

主要目的: 在没有多线程竞争的前提下,通过CAS减少重量级锁使用操作系统互斥量产生的性能消耗,说白了先自旋,不行才升级阻塞。

升级时机:当关闭偏向锁功能,或多线程竞争偏向锁会导致偏向锁升级为轻量级锁

假如线程A已经拿到锁,这时线程B又来抢该对象的锁,由于该对象的锁已经被线程A拿到,当前该锁已是偏向锁了。

而线程B在争抢时发现对象头Mark Word中的线程ID不是线程B自己的线程ID(而是线程A),那线程B就会进行CAS操作希望能获得锁。

此时线程B操作中有两种情况:

如果锁获取成功,直接替换Mark Word中的线程ID为B自己的ID(A→B),重新偏向于其他线程(即将偏向锁交给其他线程,相当于当前线程“被“释放了锁),该锁会保持偏向锁状态,A线程Over,B线程上位;

如果锁获取失败,则偏向锁升级为轻量级锁(设置偏向锁标识为0并设置锁标志位为00),此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码,而正在竞争的线程B会进入自旋等待获得该轻量级锁。

在这里插入图片描述

尼恩提示: 以上内容比较复杂,后面会在《尼恩Java面试宝典 配套视频》中,进行详细解读。

如果没有 面试机会,可以找尼恩来帮忙,打造一个绝世好简历,实现 职业逆袭中厂大龄34岁,被裁8月收一大厂offer, 年薪65W,转架构后逆天改命!

轻量级锁也是在 JDK1.6 加入的,当一个线程获取偏向锁的时候,有另外的线程加入锁的竞争时,这个时候就会从偏向锁升级为轻量级锁。

在这里插入图片描述

在轻量级锁的状态时,虚拟机首先会在当前线程的栈帧当中建立一个锁记录(Lock Record),用于存储对象 MarkWord 的拷贝,官方称这个为 Displaced Mark Word。

然后虚拟机会使用 CAS 操作尝试将对象的 MarkWord 指向栈中的 Lock Record,如果操作成功说明这个线程获取到了锁,能够进入同步代码块执行,否则说明这个锁对象已经被其他线程占用了,线程就需要使用 CAS 不断的进行获取锁的操作,当然你可能会有疑问,难道就让线程一直死循环了吗?

这对 CPU 的花费那不是太大了吗,确实是这样的因此在 CAS 满足一定条件的时候轻量级锁就会升级为重量级锁,具体过程在重量级锁章节中分析。

当线程需要从同步代码块出来的时候,线程同样的需要使用 CAS 将 Displaced Mark Word 替换回对象的 MarkWord,如果替换成功,那么同步过程就完成了,如果替换失败就说明有其他线程尝试获取该锁,而且锁已经升级为重量级锁,此前竞争锁的线程已经被挂起,因此线程在释放锁的同时还需要将挂起的线程唤醒。

Java6之前

默认启用,默认情况下自旋的次数是10次,或者自旋线程数超过cpu核数一半。

Java6之后

变为自适应自旋锁。意味着自旋的次数不是固定不变的,而是根据:拥有锁线程的状态来决定,或者同一个锁上一次自旋的时间。

线程如果自旋成功了,那下次自旋的最大次数会增加,因为JVM认为既然上次成功了,那么这一次也很大概率会成功。

反之,如果很少会自旋成功,那么下次会减少自旋的次数甚至不自旋,避免CPU空转。

在有两个以上的线程竞争同一个轻量级锁的情况下,轻量级锁不再有效(轻量级锁升级的一个条件),这个时候锁为膨胀成重量级锁,锁的标志状态变成 10,MarkWord 当中存储的就是指向重量级锁的指针,后面等待锁的线程就会被挂起。

因为这个时候 MarkWord 当中存储的已经是指向重量级锁的指针,因此在轻量级锁的情况下进入到同步代码块在出同步代码块的时候使用 CAS 将 Displaced Mark Word 替换回对象的 MarkWord 的时候就会替换失败,在前文已经提到,在失败的情况下,线程在释放锁的同时还需要将被挂起的线程唤醒。

五:如何膨胀到 重量级锁

适用于:有大量的线程参与锁的竞争,冲突性很高。

重量级锁原理

Java中synchronized的重量级锁,是基于进入和退出Monitor对象实现的。在编译时会将同步块的开始位置插入monitor enter指令,在结束位置插入monitor exit指令。

当线程执行到monitor enter指令时,会尝试获取对象所对应的Monitor所有权,如果获取到了,即获取到了锁,会在Monitor的owner中存放当前线程的id,这样它将处于锁定状态,除非退出同步块,否则其他线程无法获取到这个Monitor。

在这里插入图片描述

重量级锁就是一种开销最大的锁机制,

在这种情况下需要操作系统将 没有抢到锁的线程挂起,

JVM(Linux 操作系统下)底层是使用 pthread_mutex_lock 、 pthread_mutex_unlock 、 pthread_cond_wait 、 pthread_cond_signal 和 pthread_cond_broadcast 这几个库函数实现的,而这些函数依赖于 futex 系统调用,因此在使用重量级锁的时候因为进行了系统调用,进程需要从用户态转为内核态将线程挂起,然后从内核态转为用户态,当解锁的时候又需要从用户态转为内核态将线程唤醒,这一来二去的花费就比较大了(和 CAS 自旋锁相比)。

尼恩提示: 以上内容比较复杂,后面会在《尼恩Java面试宝典 配套视频》中,进行详细解读。

如果没有 面试机会,可以找尼恩来帮忙,打造一个绝世好简历,实现 职业逆袭中厂大龄34岁,被裁8月收一大厂offer, 年薪65W,转架构后逆天改命!

锁的内存结构变化 大总结

锁状态 bits 1bit是否是偏向锁 2bit锁标志位
无锁状态 对象的hashCode 0 01
偏向锁 线程ID 1 01
轻量级锁 指向栈中锁记录的指针 0 00
重量级锁 指向互斥量的指针 0 10

锁的膨胀流程

在这里插入图片描述

尼恩提示: 以上内容比较复杂,后面会在《尼恩Java面试宝典 配套视频》中,进行详细解读。

如果没有 面试机会,可以找尼恩来帮忙,打造一个绝世好简历,实现 职业逆袭中厂大龄34岁,被裁8月收一大厂offer, 年薪65W,转架构后逆天改命!

说在最后:有问题找老架构取经

JVM锁的膨胀、锁的内存结构变化相关的面试题,是非常常见的面试题。也是核心面试题。也是非常难面试题。

以上的内容,如果大家能对答如流,如数家珍,基本上 面试官会被你 震惊到、吸引到。最终,让面试官爱到 “不能自已、口水直流”。offer, 也就来了。

在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典》V174,在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。

另外,如果没有面试机会,可以找尼恩来帮扶、领路。尼恩已经指导了大量的就业困难的小伙伴上岸.

前段时间,帮助一个40岁+就业困难小伙伴拿到了一个年薪100W的offer,小伙伴实现了 逆天改命

尼恩技术圣经系列PDF

……完整版尼恩技术圣经PDF集群,请找尼恩领取

《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF,请到下面公号【技术自由圈】取↓↓↓

相关文章
|
2月前
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
25天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
23天前
|
Java Linux Windows
JVM内存
首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制。
18 1
|
23天前
|
SQL 关系型数据库 MySQL
阿里面试:1000万级大表, 如何 加索引?
45岁老架构师尼恩在其读者交流群中分享了如何在生产环境中给大表加索引的方法。文章详细介绍了两种索引构建方式:在线模式(Online DDL)和离线模式(Offline DDL),并深入探讨了 MySQL 5.6.7 之前的“影子策略”和 pt-online-schema-change 方案,以及 MySQL 5.6.7 之后的内部 Online DDL 特性。通过这些方法,可以有效地减少 DDL 操作对业务的影响,确保数据的一致性和完整性。尼恩还提供了大量面试题和解决方案,帮助读者在面试中充分展示技术实力。
|
2月前
|
消息中间件 存储 canal
阿里面试:canal+MQ,会有乱序的问题吗?
本文详细探讨了在阿里面试中常见的问题——“canal+MQ,会有乱序的问题吗?”以及如何保证RocketMQ消息有序。文章首先介绍了消息有序的基本概念,包括全局有序和局部有序,并分析了RocketMQ中实现消息有序的方法。接着,针对canal+MQ的场景,讨论了如何通过配置`canal.mq.partitionsNum`和`canal.mq.partitionHash`来保证数据同步的有序性。最后,提供了多个与MQ相关的面试题及解决方案,帮助读者更好地准备面试,提升技术水平。
阿里面试:canal+MQ,会有乱序的问题吗?
|
2月前
|
存储 算法 Java
聊聊jvm的内存结构, 以及各种结构的作用
【10月更文挑战第27天】JVM(Java虚拟机)的内存结构主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和运行时常量池。各部分协同工作,为Java程序提供高效稳定的内存管理和运行环境,确保程序的正常执行、数据存储和资源利用。
50 10
|
2月前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
2月前
|
消息中间件 架构师 Java
阿里面试:秒杀的分布式事务, 是如何设计的?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴在面试阿里、滴滴、极兔等一线互联网企业时,遇到了许多关于分布式事务的重要面试题。为了帮助大家更好地应对这些面试题,尼恩进行了系统化的梳理,详细介绍了Seata和RocketMQ事务消息的结合,以及如何实现强弱结合型事务。文章还提供了分布式事务的标准面试答案,并推荐了《尼恩Java面试宝典PDF》等资源,帮助大家在面试中脱颖而出。
|
2月前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
2月前
|
Kubernetes 架构师 算法
阿里面试:全国14亿人,统计出重名最多的前100个姓名
文章介绍了如何解决“从全国14亿人的数据中统计出重名人数最多的前100位姓名”的面试题,详细分析了多种数据结构的优缺点,最终推荐使用前缀树(Trie)+小顶堆的组合。文章还提供了具体的Java代码实现,并讨论了在内存受限情况下的解决方案,强调了TOP N问题的典型解题思路。最后,鼓励读者通过系统化学习《尼恩Java面试宝典》提升面试技巧。
阿里面试:全国14亿人,统计出重名最多的前100个姓名

相关实验场景

更多