慧策(掌上先机)一面 2023.6.12

自我介绍

聊天 + 八股 以聊天的形式问的,体验很好

对学校情况的一些了解

1.currentHashMap底层实现(简历第二行写的并发,看简历上有currentHashMap问的)

ConcurrentHashMap 1.7

  • 数据结构:
  • 提供了一个segment数组,在初始化ConcurrentHashMap 的时候可以指定数组的长度,默认是16,一旦初始化之后中间不可扩容
  • 在每个segment中都可以挂一个HashEntry数组,数组里面可以存储具体的元素,HashEntry数组是可以扩容的
  • 在HashEntry存储的数组中存储的元素,如果发生冲突,则可以挂单向链表

存储流程

  • 先去计算key的hash值,然后确定segment数组下标
  • 再通过hash值确定hashEntry数组中的下标存储数据
  • 在进行操作数据的之前,会先判断当前segment对应下标位置是否有线程进行操作,为了线程安全使用的是ReentrantLock进行加锁,如果获取锁是被会使用cas自旋锁进行尝试
  • 并发度:Segment 数组大小即并发度,决定了同一时刻最多能有多少个线程并发访问。Segment 数组不能扩容,意味着并发度在 ConcurrentHashMap 创建时就固定了
  • 索引计算
  • 扩容:每个小数组的扩容相对独立,小数组在超过扩容因子时会触发扩容,每次扩容翻倍
  • Segment[0] 原型:首次创建其它小数组时,会以此原型为依据,数组长度,扩容因子都会以原型为准

小数组容量为 容量/并发度

ConcurrentHashMap 1.8

  • 在JDK1.8中,放弃了Segment臃肿的设计,数据结构跟HashMap的数据结构是一样的:数组+红黑树+链表采用 CAS + Synchronized来保证并发安全进行实现
  • 并发度:Node 数组有多大,并发度就有多大,与 1.7 不同,Node 数组可以扩容
  • 扩容条件:Node 数组满 3/4 时就会扩容
  • 扩容单位:以链表为单位从后向前迁移链表,迁移完成的将旧数组头节点替换为 ForwardingNode
  • 扩容时并发 get
  • 扩容时并发 put
  • 与 1.7 相比是懒惰初始化
  • capacity 代表预估的元素个数,capacity / factory 来计算出初始数组大小,需要贴近
  • loadFactor 只在计算初始数组大小时被使用,之后扩容固定为 3/4
  • 超过树化阈值时的扩容问题,如果容量已经是 64,直接树化,否则在原来容量基础上做 3 轮扩容

2.map的数据结构了解吗

3.hashmap的实现

讲了put方法

  1. 判断键值对数组table是否为空或为null,否则执行resize()进行扩容(初始化)
  2. 根据键值key计算hash值得到数组索引
  3. 判断table[i]==null,条件成立,直接新建节点添加
  4. 如果table[i]==null ,不成立

4.1 判断table[i]的首个元素是否和key一样,如果相同直接覆盖value

4.2 判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对

4.3 遍历table[i],链表的尾部插入数据,然后判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操 作,遍历过程中若发现key已经存在直接覆盖value

  1. 插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold(数组长度*0.75),如果超过,进行扩容。

扩容讲错了

  • 添加元素或初始化的时候需要调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次每次扩容都是达到了扩容阈值(数组长度 * 0.75)
  • 每次扩容的时候,都是扩容之前容量的2倍;
  • 扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新的数组中
  • 没有hash冲突的节点,则直接使用 e.hash & (newCap - 1) 计算新数组的索引位置
  • 如果是红黑树,走红黑树的添加
  • 如果是链表,则需要遍历链表,可能需要拆分链表,判断(e.hash & oldCap)是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上

4.hashmap为什么线程不安全

具体来说,当多个线程同时对HashMap进行写操作时,可能会导致数据覆盖或者数据丢失的问题。例如,当两个线程同时向HashMap中添加一个键值对时,它们可能会同时计算出相同的哈希值,从而导致数据覆盖的问题。又或者,当一个线程正在遍历HashMap的同时,另一个线程对HashMap进行修改,可能会导致ConcurrentModificationException异常的抛出。

5.currentHashMap如何实现线程安全的(其实我感觉一开始讲了,可能讲的不细)

6.有哪几种垃圾回收器?和他们的特点?1.8默认的垃圾回收器(ps + po)

记不清,讲的不好

7.然后讲的太细了, 标记清除、标记复制和标记整理。标记清除是哪个垃圾修堆器使用的一个方法

不清楚

8.类加载机制

9.jvm内存结构

10.哪些线程共享,哪些不共享

线程私有的:

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈

线程共享的:

  • 方法区
  • 直接内存 (非运行时数据区的一部分)

11.gc发生在哪里

12.方法区会被gc吗

会(答错了)

13.栈帧的结构

局部变量表、操作数栈、动态链接、方法返回地址

14.程序进栈出栈的过程(不会,他说这个问题太难了)(栈的大小多少合适,这种问题)

  1. 当一个函数被调用时,会为该函数创建一个栈帧(Stack Frame),栈帧包含了该函数的参数、局部变量、返回地址等信息。
  2. 创建好栈帧后,将其压入调用栈(Call Stack)中,成为当前栈帧,同时将程序计数器(Program Counter)指向该函数的第一条指令,开始执行该函数。
  3. 在函数执行过程中,如果遇到其他函数的调用,会重复上述过程,为该函数创建一个新的栈帧,将其压入调用栈中,成为当前栈帧。
  4. 如果函数执行过程中遇到了返回语句,会将当前栈帧弹出调用栈,同时将程序计数器指向返回地址,继续执行调用该函数的函数。
  5. 当调用栈为空时,程序执行结束。

15.java内存的布局

在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:对象头实例数据对齐填充

Hotspot 虚拟机的对象头包括两部分信息第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),

hashcode:25位的对象标识Hash码

age:对象分代年龄占4位

biased_lock:偏向锁标识,占1位 ,0表示没有开始偏向锁,1表示开启了偏向锁

thread:持有偏向锁的线程ID,占23位

epoch:偏向时间戳,占2位

ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针,占30位

ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针,

占30位

我们可以通过lock的标识,来判断是哪一种锁的等级

后三位是001表示无锁

后三位是101表示偏向锁

后两位是00表示轻量级锁

后两位是10表示重量级锁

另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。

对齐填充部分不是必然存在的,也没有什么特别的含义,仅仅起占位作用。 因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,换句话说就是对象的大小必须是 8 字节的整数倍。而对象头部分正好是 8 字节的倍数(1 倍或 2 倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

16.jvm是如何判断他是垃圾的

引用计数法 可达性分析算法(面试官补充了python的计数算法)

17.对象被判定为不可达后对象就死了吗?

对象被判定为不可达后,并不代表对象就死了。

在Java中,对象的生命周期由垃圾回收器来管理。当一个对象不再被任何强引用、软引用、弱引用和虚引用所引用时,就会被判定为不可达(Unreachable)状态。在下一次垃圾回收时,垃圾回收器会对不可达对象进行标记、清理和回收操作。

具体来说,当一个对象被判定为不可达时,垃圾回收器会首先对该对象进行标记操作,将其标记为待清理状态。接着,垃圾回收器会对待清理对象进行清理操作,释放其占用的内存空间。最后,垃圾回收器会对被清理对象进行回收操作,将其从内存中彻底删除。

需要注意的是,垃圾回收器的标记、清理和回收操作并不是一次性完成的,而是分阶段进行的。在标记阶段,垃圾回收器会标记所有可达对象,而不会对不可达对象进行标记。在清理阶段,垃圾回收器会对待清理对象进行清理操作,而不会立即回收被清理对象的内存空间。在回收阶段,垃圾回收器会将被清理对象的内存空间回收,以便下次分配内存时使用。

因此,对象被判定为不可达后,并不代表对象就死了,而是需要经过垃圾回收器的标记、清理和回收操作,才能真正从内存中删除。

18.对象的几个阶段 给个开头创建阶段、应用阶段

没听说过

创建阶段、应用阶段、不可见阶段、不可达阶段,还有搜集阶段、终结阶段还有重新分配

在Java中,对象的生命周期可以分为多个阶段,包括创建阶段、应用阶段、不可见阶段、不可达阶段、搜集阶段、终结阶段和重新分配阶段。下面是各个阶段的具体说明:

1. 创建阶段:对象的创建阶段是指从类加载到对象分配内存空间、初始化零值、设置对象头、执行构造函数等一系列步骤,直到对象被创建成功。

2. 应用阶段:对象的应用阶段是指对象被程序所使用的过程。在应用阶段,对象可能会被多次引用和修改,直到程序不再需要该对象时,对象进入下一个阶段。

3. 不可见阶段:对象的不可见阶段是指对象不再被程序所引用和访问,但是仍然可以通过其他途径访问到该对象,例如通过弱引用或虚引用。

4. 不可达阶段:对象的不可达阶段是指对象不再被任何强引用、软引用、弱引用和虚引用所引用时,被判定为不可达状态。在下一次垃圾回收时,垃圾回收器会对不可达对象进行标记、清理和回收操作。

5. 搜集阶段:对象的搜集阶段是指垃圾回收器对内存中的不可达对象进行标记、清理和回收操作的过程。搜集阶段是垃圾回收的核心过程,可以分为标记、清理和压缩等多个阶段。

6. 终结阶段:对象的终结阶段是指对象在被垃圾回收器回收前,执行finalize()方法的过程。在finalize()方法中,可以进行一些资源释放和清理操作。

7. 重新分配阶段:对象的重新分配阶段是指在垃圾回收器回收内存后,将空闲内存重新分配给新的对象的过程。在重新分配阶段,JVM会对内存进行整理和压缩,以提高内存的利用效率。

需要注意的是,对象的生命周期是由垃圾回收器来管理的,程序员无法直接控制对象的各个阶段。在实际应用中,应该尽量避免对象的过早创建和过晚销毁,以减少内存占用和垃圾回收的开销,提高程序的性能和效率。

19.finalize方法

20.你有一个对象,我想把它显示的设为不可不可见阶段应该怎么做?

不会

将该对象的所有引用都设置为null

21.事务级别?默认是那种?

22.什么是幻读?如何解决幻读?

23.MySQL的锁

24.间隙锁和临建锁是解决什么问题的?

25.MySQL的数据结构,是怎么存储数据的

算法 实现加法 忘了,没写出来

反问

多久出结果

学习建议 建议好好算法

实习生干些什么

业务介绍

另:6.13 oc

6.14 offer

有没有去过的佬讲讲公司怎么样 看网上说不好

部门平台集成

#我的实习求职记录##晒一晒我的offer#
全部评论
感觉这个问的太细了
2 回复 分享
发布于 2023-06-22 14:51 湖南
我完全没问八股,面试官是先从项目开始,问我学习一门技术是如何学习的、让我介绍一门我最熟悉的技术。还有两个开放式的问题,你觉得一个成功的人或者是团队有什么样的特质?如果你和同事或者是leader或者你是leader发生了冲突你会怎么解决?方向是做供应链的
1 回复 分享
发布于 2023-06-15 14:42 四川
问了好多啊
点赞 回复 分享
发布于 2023-10-13 21:09 北京
楼主去了吗,我也oc了,但是听HR说北京租房压力很大有点犹豫
点赞 回复 分享
发布于 2023-07-07 18:08 山东
方法区会发生GC,以下内容摘自深入理解Java虚拟机。 些人认为方法区(如HotSpot虚拟机中的元空间或者永久代)是没有垃圾收集行为的,《Java虚拟机规范》中提到过可以不要求虚拟机在方法区中实现垃圾收集,事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如JDK 11时期的ZGC收集器就不支持类卸载),方法区垃圾收集的“性价比”通常也是比较低的:在Java堆中,尤其是在新生代中,对常规应用进行一次垃圾收集通常可以回收70%至99%的内存空间,相比之下,方法区回收囿于苛刻的判定条件,其区域垃圾收集的回 收成果往往远低于此。 方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。回收废弃常量与回收Java堆中的对象非常类似。举个常量池中字面量回收的例子,假如一个字符串“java”曾经进入常量池中,但是当前系统又没有任何一个字符串对象的值是“java”,换句话说,已经没有任何字符串对象引用常量池中的“java”常量,且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收,而且垃圾收集器判断确有必要的话,这个“java”常量就将会被系统清理出常量池。常量池中其他类(接 口)、方法、字段的符号引用也与此类似。 判定一个常量是否“废弃”还是相对简单,而要判定一个类型是否属于“不再被使用的类”的条件就比较苛刻了。需要同时满足下面三个条件: ·该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。 ·加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。 ·该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。 Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是“被允许”,而并不是和对象一样,没有引用了就必然会回收。
点赞 回复 分享
发布于 2023-06-21 00:12 安徽
哥们儿是面的北京的吗
点赞 回复 分享
发布于 2023-06-15 20:39 河北
我今天面
点赞 回复 分享
发布于 2023-06-15 13:53 四川
ConcurrentHashMap的扩容机制是怎样的?
点赞 回复 分享
发布于 2023-06-15 10:18 上海
16个segment数组,这个数字是怎么来的呢?
点赞 回复 分享
发布于 2023-06-15 10:03 江苏

相关推荐

11-27 14:21
同济大学 Java
卢来猴祖:给了这薪资关键拿不了几个月就给你踹了呀
点赞 评论 收藏
分享
评论
13
63
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务