写点什么

大厂面试题集合之阿里一面 [1]

作者:派大星
  • 2023-12-21
    北京
  • 本文字数:2001 字

    阅读完需:约 7 分钟

说一下 ArrayList 和 LinkedList 区别

  1. 首先,他们的底层数据结构不同,ArrayList 底层是基于数组实现的,LinkedList 底层是基于链表实现的

  2. 由于底层数据结构不同,他们所适用的场景也不同,ArrayList 更适合随机查找,LinkedList 更适合删除和添加,查询、添加、删除的时间复杂度不同

  3. 另外 ArrayList 和 LinkedList 都实现了 List 接口,但是 LinkedList 还额外实现了 Deque 接口,所以 LinkedList 还可以当做队列来使用

说一下 HashMap 的 Put 方法

先说 HashMap 的 Put 方法的大体流程:

  1. 根据 Key 通过哈希算法与与运算得出数组下标

  2. 如果数组下标位置元素为空,则将 key 和 value 封装为 Entry 对象(JDK1.7 中是 Entry 对象,JDK1.8 中是 Node 对象)并放入该位置

  3. 如果数组下标位置元素不为空,则要分情况讨论如果是 JDK1.7,则先判断是否需要扩容,如果要扩容就进行扩容,如果不用扩容就生成 Entry 对象,并使用头插法添加到当前位置的链表中如果是 JDK1.8,则会先判断当前位置上的 Node 的类型,看是红黑树 Node,还是链表 Node 如果是红黑树 Node,则将 key 和 value 封装为一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前 key,如果存在则更新 value 如果此位置上的 Node 对象是链表节点,则将 key 和 value 封装为一个链表 Node 并通过尾插法插入到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会判断是否存在当前 key,如果存在则更新 value,当遍历完链表后,将新链表 Node 插入到链表中,插入到链表后,会看当前链表的节点个数,如果大于等于 8,那么则会将该链表转成红黑树将 key 和 value 封装为 Node 插入到链表或红黑树中后,再判断是否需要进行扩容,如果需要就扩容,如果不需要就结束 PUT 方法

说一下 ThreadLocal

  1. ThreadLocal 是 Java 中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据

  2. ThreadLocal 底层是通过 ThreadLocalMap 来实现的,每个 Thread 对象(注意不是 ThreadLocal 对象)中都存在一个 ThreadLocalMap,Map 的 key 为 ThreadLocal 对象,Map 的 value 为需要缓存的值

  3. 如果在线程池中使用 ThreadLocal 会造成内存泄漏,因为当 ThreadLocal 对象使用完之后,应该要把设置的 key,value,也就是 Entry 对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向 ThreadLocalMap,ThreadLocalMap 也是通过强引用指向 Entry 对象,线程不被回收,Entry 对象也就不会被回收,从而出现内存泄漏,解决办法是,在使用了 ThreadLocal 对象之后,手动调用 ThreadLocal 的 remove 方法,手动清楚 Entry 对象

  4. ThreadLocal 经典的应用场景就是连接管理(一个线程持有一个连接,该连接对象可以在不同的方法之间进行传递,线程之间不共享同一个连接

说一下 JVM 中,哪些是共享区,哪些可以作为 gc root

  1. 堆区和方法区是所有线程共享的,栈、本地方法栈、程序计数器是每个线程独有的

image.png

  1. 什么是 gc root,JVM 在进行垃圾回收时,需要找到“垃圾”对象,也就是没有被引用的对象,但是直接找“垃圾”对象是比较耗时的,所以反过来,先找“非垃圾”对象,也就是正常对象,那么就需要从某些“根”开始去找,根据这些“根”的引用路径找到正常对象,而这些“根”有一个特征,就是它只会引用其他对象,而不会被其他对象引用,例如:栈中的本地变量、方法区中的静态变量、本地方法栈中的变量、正在运行的线程等可以作为 gc root。

你们项目如何排查 JVM 问题

对于还在正常运行的系统:

  1. 可以使用 jmap 来查看 JVM 中各个区域的使用情况

  2. 可以通过 jstack 来查看线程的运行情况,比如哪些线程阻塞、是否出现了死锁

  3. 可以通过 jstat 命令来查看垃圾回收的情况,特别是 fullgc,如果发现 fullgc 比较频繁,那么就得进行调优了

  4. 通过各个命令的结果,或者 jvisualvm 等工具来进行分析

  5. 首先,初步猜测频繁发送 fullgc 的原因,如果频繁发生 fullgc 但是又一直没有出现内存溢出,那么表示 fullgc 实际上是回收了很多对象了,所以这些对象最好能在 younggc 过程中就直接回收掉,避免这些对象进入到老年代,对于这种情况,就要考虑这些存活时间不⻓的对象是不是比较大,导致年轻代放不下,直接进入到了老年代,尝试加大年轻代的大小,如果改完之后,fullgc 减少,则证明修改有效

  6. 同时,还可以找到占用 CPU 最多的线程,定位到具体的方法,优化这个方法的执行,看是否能避免某些对象的创建,从而节省内存

对于已经发生了 OOM 的系统:

  1. 一般生产系统中都会设置当系统发生了 OOM 时,生成当时的 dump 文件(-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base)

  2. 我们可以利用 jsisualvm 等工具来分析 dump 文件

  3. 根据 dump 文件找到异常的实例对象,和异常的线程(占用 CPU 高),定位到具体的代码

  4. 然后再进行详细的分析和调试

总之,调优不是一蹴而就的,需要分析、推理、实践、总结、再分析,最终定位到具体的问题

往期精彩系列推荐

如有问题,欢迎加微信交流:w714771310,备注- 技术交流  。或关注微信公众号【码上遇见你】。



发布于: 刚刚阅读数: 3
用户头像

派大星

关注

微信搜索【码上遇见你】,获取更多精彩内容 2021-12-13 加入

微信搜索【码上遇见你】,获取更多精彩内容

评论

发布
暂无评论
大厂面试题集合之阿里一面[1]_Java 面试题_派大星_InfoQ写作社区