前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >阿里今年的年终奖。。

阿里今年的年终奖。。

作者头像
沉默王二
发布2024-05-06 14:47:15
1060
发布2024-05-06 14:47:15
举报
文章被收录于专栏:沉默王二

大家好,我是二哥呀。

阿里面经(八股吟唱开始)

今天是五一假期的第一天,仍然有同学在继续准备面试,他已经面了阿里的大部分部门,包括前面提到的饿了吗、阿里妈妈、支付宝、阿里国际等等。这次我们就以《Java 面试指南》中同学 5 为例,来看看阿里的面试官都喜欢问哪些问题。

Java 面试指南专栏

还在冲刺的同学可以收藏起来,好做到知彼知己百战不殆。

说说Java的并发系统(从悲观锁聊到乐观锁,还有线程、线程池之类的,聊了快十分钟这个)

聊聊悲观锁和乐观锁?

对于悲观锁来说,它总是认为每次访问共享资源时会发生冲突,所以必须对每次数据操作加上锁,以保证临界区的程序同一时间只能有一个线程在执行。

悲观锁的代表有 synchronized 关键字和 Lock 接口:

  • synchronized:可以修饰方法或代码块,保证同一时刻只有一个线程执行该代码段。
  • ReentrantLock:一种可重入的互斥锁,重入的意思是能够对共享资源重复加锁,即当前线程获取该锁后再次获取不会被阻塞。

乐观锁,顾名思义,它是乐观派。乐观锁总是假设对共享资源的访问没有冲突,线程可以不停地执行,无需加锁也无需等待。一旦多个线程发生冲突,乐观锁通常使用一种称为 CAS 的技术来保证线程执行的安全性。

由于乐观锁假想操作中没有锁的存在,因此不太可能出现死锁的情况,换句话说,乐观锁天生免疫死锁。

  • 乐观锁多用于“读多写少“的环境,避免频繁加锁影响性能;
  • 悲观锁多用于”写多读少“的环境,避免频繁失败和重试影响性能。
什么是线程?

线程说简单点就是我们在 Java 程序中启动的一个 main 线程,一个进程至少会有一个线程。当然了,我们也可以启动多个线程,比如说一个线程进行 IO 读写,一个线程进行加减乘除计算,这样就可以充分发挥多核 CPU 的优势,因为 IO 读写相对 CPU 计算来说慢得多。线程是 CPU 分配资源的基本单位。

什么是线程池?

线程池,简单来说,就是一个管理线程的池子。

三分恶面渣逆袭:管理线程的池子

①、频繁地创建和销毁线程会消耗系统资源,线程池能够复用已创建的线程。

②、提高响应速度,当任务到达时,任务可以不需要等待线程创建就立即执行。

③、线程池支持定时执行、周期性执行、单线程执行和并发数控制等功能。

一个8G内存的系统最多能创建多少线程?(奇怪的问题,答了一些pcb、页表、虚拟机栈什么的)

在确定一个系统最多可以创建多个线程时,除了需要考虑系统的内存大小外,Java 虚拟机栈的大小也是值得考虑的因素。

线程在创建的时候会被分配一个虚拟机栈,在 64 位操作系统中,默认大小为 1M。

通过 java -XX:+PrintFlagsFinal -version | grep ThreadStackSize 这个命令可以查看 JVM 栈的默认大小。

二哥的 Java 进阶之路:默认的虚拟机栈大小

其中 ThreadStackSize 的单位是字节,也就是说默认的 JVM 栈大小是 1024 KB,也就是 1M。

换句话说,8GB = 8 * 1024 MB = 8 * 1024 * 1024 KB,所以一个 8G 内存的系统可以创建的线程数为 8 * 1024 = 8192 个。

但操作系统本身的运行也需要消耗一定的内存,所以实际上可以创建的线程数肯定会比 8192 少一些。

可以通过下面这段代码来验证一下:

代码语言:javascript
复制
public class StackOverflowErrorTest1 {
    private static AtomicInteger count = new AtomicInteger(0);
    public static void main(String[] args) {
        while (true) {
            testStackOverflowError();
        }
    }

    public static void testStackOverflowError() {
        System.out.println(count.incrementAndGet());
        testStackOverflowError();
    }
}

启动一个Java程序,你能说说里面有哪些线程吗?

首先是 main 线程,这是程序开始执行的入口。

然后是垃圾回收线程,它是一个后台线程,负责回收不再使用的对象。

还有编译器线程,在及时编译中(JIT),负责把一部分热点代码编译后放到 codeCache 中,以提升程序的执行效率。

二哥的 Java 进阶之路:JIT

可以通过下面这段代码进行检测:

代码语言:javascript
复制
class ThreadLister {
    public static void main(String[] args) {
        // 获取所有线程的堆栈跟踪
        Map<Thread, StackTraceElement[]> threads = Thread.getAllStackTraces();
        for (Thread thread : threads.keySet()) {
            System.out.println("Thread: " + thread.getName() + " (ID=" + thread.getId() + ")");
        }
    }
}

结果如下所示:

代码语言:javascript
复制
Thread: Monitor Ctrl-Break (ID=5)
Thread: Reference Handler (ID=2)
Thread: main (ID=1)
Thread: Signal Dispatcher (ID=4)
Thread: Finalizer (ID=3)

简单解释下:

  • Thread: main (ID=1) - 主线程,Java 程序启动时由 JVM 创建。
  • Thread: Reference Handler (ID=2) - 这个线程是用来处理引用对象的,如软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference)。负责清理被 JVM 回收的对象。
  • Thread: Finalizer (ID=3) - 终结器线程,负责调用对象的 finalize 方法。对象在垃圾回收器标记为可回收之前,由该线程执行其 finalize 方法,用于执行特定的资源释放操作。
  • Thread: Signal Dispatcher (ID=4) - 信号调度线程,处理来自操作系统的信号,将它们转发给 JVM 进行进一步处理,例如响应中断、停止等信号。
  • Thread: Monitor Ctrl-Break (ID=5) - 监视器线程,通常由一些特定的 IDE 创建,用于在开发过程中监控和管理程序执行或者处理中断。

哪些情况下对象会进入老年代?

对象通常会现在年轻代中分配,然后随着时间的推移和垃圾收集的处理,某些对象会进入到老年代中。

三分恶面渣逆袭:对象进入老年代

①、长期存活的对象将进入老年代

对象在年轻代中存活足够长的时间(即经过足够多的垃圾回收周期)后,会晋升到老年代。

每次 GC 未被回收的对象,其年龄会增加。当对象的年龄超过一个特定阈值(默认通常是 15),它就会被移动到老年代。这个年龄阈值可以通过 JVM 参数-XX:MaxTenuringThreshold来设置。

②、大对象直接进入老年代

为了避免在年轻代中频繁复制大对象,JVM 提供了一种策略,允许大对象直接在老年代中分配。

这些是所谓的“大对象”,其大小超过了预设的阈值(由 JVM 参数-XX:PretenureSizeThreshold控制)。直接在老年代分配可以减少在年轻代和老年代之间的数据复制。

③、动态对象年龄判定

除了固定的年龄阈值,还会根据各个年龄段对象的存活大小和总空间等因素动态调整对象的晋升策略。

如果在 Survivor 空间中相同年龄的所有对象大小总和大于 Survivor 空间的一半,那么年龄大于或等于该年龄的对象就可以直接进入老年代。

full gc 和 young gc 的区别

Minor GC 也称为 Young GC,是指发生在年轻代(Young Generation)的垃圾收集。年轻代包含 Eden 区以及两个 Survivor 区。

二哥的 Java 进阶之路:Java 堆划分

Full GC 是最彻底的垃圾收集,涉及整个 Java 堆和方法区(或元空间)。它是最耗时的 GC,通常在 JVM 压力很大时发生。

索引的分类,创建索引的最佳实践

我从数据结构上来说吧。

①、B+树索引:最常见的索引类型,一种将索引值按照一定的算法,存入一个树形的数据结构中(二叉树),每次查询都从树的根节点开始,一次遍历叶子节点,找到对应的值。查询效率是 O(logN)。

也是 InnoDB 存储引擎的默认索引类型

B+ 树是 B 树的升级版,B+ 树中的非叶子节点都不存储数据,只存储索引。叶子节点中存储了所有的数据,并且构成了一个从小到大的有序双向链表,使得在完成一次树的遍历定位到范围查询的起点后,可以直接通过叶子节点间的指针顺序访问整个查询范围内的所有记录,而无需对树进行多次遍历。这在处理大范围的查询时特别高效。

一颗剽悍的种子:B+树的结构

因为 B+ 树是 InnoDB 的默认索引类型,所以创建 B+ 树的时候不需要指定索引类型。

代码语言:javascript
复制
CREATE TABLE example_btree (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255),
    INDEX name_index (name)
) ENGINE=InnoDB;

②、Hash 索引:基于哈希表的索引,查询效率可以达到 O(1),但是只适合 = 和 in 查询,不适合范围查询。

Hash 索引在原理上和 Java 中的 HashMap 类似,当发生哈希冲突的时候也是通过拉链法来解决。

业余码农:哈希索引

可以通过下面的语句创建哈希索引:

代码语言:javascript
复制
CREATE TABLE example_hash (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255),
    UNIQUE HASH (name)
) ENGINE=MEMORY;

注意,我们这里创建的是 MEMORY 存储引擎,InnoDB 并不提供直接创建哈希索引的选项,因为 B+ 树索引能够很好地支持范围查询和等值查询,满足了大多数数据库操作的需要。

不过,InnoDB 存储引擎内部使用了一种名为“自适应哈希索引”(Adaptive Hash Index, AHI)的技术。

自适应哈希索引并不是由用户显式创建的,而是 InnoDB 根据数据访问的模式自动建立和管理的。当 InnoDB 发现某个索引被频繁访问时,会在内存中创建一个哈希索引,以加速对这个索引的访问。

可以通过下面的语句查看自适应哈希索引的状态:

代码语言:javascript
复制
SHOW VARIABLES LIKE 'innodb_adaptive_hash_index';

如果返回的值是 ON,说明自适应哈希索引是开启的。

二哥的 Java 进阶之路

尽管索引能提高查询性能,但不当的使用也会带来一系列问题。在加索引时需要注意以下几点:

①、选择合适的列作为索引

  • 经常作为查询条件(WHERE 子句)、排序条件(ORDER BY 子句)、分组条件(GROUP BY 子句)的列是建立索引的好候选。
  • 区分度低的字段,例如性别,不要建索引
  • 频繁更新的字段,不要作为主键或者索引
  • 不建议用无序的值(例如身份证、UUID )作为索引,当主键具有不确定性,会造成叶子节点频繁分裂,出现磁盘存储的碎片化

②、避免过多的索引

  • 每个索引都需要占用额外的磁盘空间。
  • 更新表(INSERT、UPDATE、DELETE 操作)时,所有的索引都需要被更新。
  • 维护索引文件需要成本;还会导致页分裂,IO 次数增多。

③、利用前缀索引和索引列的顺序

  • 对于字符串类型的列,可以考虑使用前缀索引来减少索引大小。
  • 在创建复合索引时,应该根据查询条件将最常用作过滤条件的列放在前面。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 沉默王二 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 阿里面经(八股吟唱开始)
    • 说说Java的并发系统(从悲观锁聊到乐观锁,还有线程、线程池之类的,聊了快十分钟这个)
      • 聊聊悲观锁和乐观锁?
      • 什么是线程?
      • 什么是线程池?
    • 一个8G内存的系统最多能创建多少线程?(奇怪的问题,答了一些pcb、页表、虚拟机栈什么的)
      • 启动一个Java程序,你能说说里面有哪些线程吗?
        • 哪些情况下对象会进入老年代?
          • full gc 和 young gc 的区别
            • 索引的分类,创建索引的最佳实践
            相关产品与服务
            对象存储
            对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档