越凡创新-成都-线下面试-Java
越凡创新-成都-线下面试
spi
类加载器
类加载的几个过程:加载、验证、准备、解析、初始化。然后是使用和卸载了
1. 加载(Loading) 加载阶段是将类的字节码文件加载到内存中,并创建一个对应的Class对象。
2. 验证(Verification) 验证阶段会对字节码进行验证,以确保其符合Java虚拟机规范。
3. 准备(Preparation) 准备阶段是为类的静态变量分配内存空间,并设置默认初始值。
4. 解析(Resolution) 解析阶段将符号引用转换为直接引用。
5. 初始化(Initialization) 初始化阶段是对类的静态变量进行赋值和静态代码块的执行。
什么是类加载器,类加载器有哪些?
通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。
主要有一下四种类加载器:
1. 启动类加载器(Bootstrap ClassLoader)加载 java 核心类库
2. 扩展类加载器(extensions class loader):加载 Java 的扩展库。
3. 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH) 来加载 Java类。
4. 用户自定义类加载器,通过继承 java.lang.ClassLoader 类的方式实现。
类加载器双亲委派模型机制?
核心思想是:当一个类加载器(Child,子类加载器)收到加载类的请求时,它首先会委托给父类加载器(Parent,父类加载器)进行加载,只有当父类加载器无法完成加载时,子类加载器才会尝试加载。这个模型有助于维护类加载器之间的层次关系,防止类的重复加载,提高类加载的效率,同时确保 Java 核心类库的安全性。
怎么打破双亲委派模型?
自定义类加载器,继承ClassLoader类,重写loadClass()方法和findClass()方法;
package包与类加载器的关联
jvm分区
1、内存模型以及分区:JVM 分为栈区、堆区和方法区
初始化的对象放在堆里面,引用放在栈里面, class 类信息、常量池(static 常量)放在方法区。
1. 栈:栈的结构是栈帧组成的,调用一个方法就压入一帧,帧上面存储局部变量,局部变量存放8大基础类型和指向引用类型的指针。
2. 堆:初始化的对象,类的成员变量 (那种非 static 的变量),所有的对象实例都在堆上分配。
3. 方法区:主要是存储类信息,常量池(static 常量),编译后的代码(字节码)等数据。
2. 堆里面的分区:Eden,survival (from+ to)
堆里面分为新生代和老生代(java8 取消了永久代,采用了 Metaspace)
新生代包含 Eden 和 Survivor,Survivor 区里面分为 from区 和 to 区。内存回收时,如果用的是复制算法,从 from 复制到 to。当经过一次或者多次 GC 之后,存活下来的对象会被移动到老年区,当 JVM 内存不够用的时候,会触发 GC,清理 JVM 老年区。当新生区满了之后会触发GC,先把存活的对象放到其中一个 Survice 区,然后进行垃圾清理。
3. GC 的两种判定方法
1. 引用计数法:指的是如果某个地方引用了这个对象就+1,如果失效了就-1,当为 0 就会回收但是 JVM没有用这种方式,因为无法判定相互循环引用(A 引用 B,B 引用 A) 的情况。
2. 引用链法: 通过一种 GC ROOT 来判断,如果有一条链不能到达 GC ROOT 就说明可以回收。
gc回收机制
1. 标记-清除(Mark and Sweep): 这是最基本的垃圾回收算法。它分为两个阶段:标记阶段和清除阶段。在标记阶段,垃圾收集器标记出所有活动对象。在清除阶段,垃圾收集器清除所有未标记的对象,即未被标记为垃圾的对象。
2. 复制(Copying): 这个算法将堆分为两个区域,一半用于存活对象,一半用于非存活对象。在垃圾回收时,将存活对象复制到另一半区域,然后清除当前区域的所有对象。这种算法简单高效,但浪费了一半的内存。
3. 标记-整理(Mark and Compact): 结合了标记-清除和复制两种算法的思想。标记阶段用于标记存活对象,然后将存活对象移动到一端,清理掉其他未标记的对象。这样可以保持对象在内存中的紧凑排列,减少碎片化。
4. 分代收集(Generational Collection): 将堆分为年轻代和老年代。大部分对象在年轻代中被分配,年轻代的垃圾回收频率较高。当对象在年轻代存活一段时间后,就会被移动到老年代。年轻代通常使用复制算法,而老年代使用标记-整理算法。
连接池的大小与数据库有什么关联
连接池的大小与数据库之间存在一定的关联,特别是在应用程序与数据库之间频繁建立和关闭连接的情况下。连接池是一种管理数据库连接的机制,它允许应用程序在需要时从池中获取连接,而不是每次都建立新的连接。以下是连接池大小与数据库性能和资源利用之间的关联:
1. 性能优化: 连接池的大小直接影响到应用程序对数据库连接的获取和释放效率。如果连接池大小适当,可以避免频繁地建立和关闭连接,从而减少了连接的开销,提高了数据库访问的性能。
2. 资源利用: 连接池的大小也与系统资源的有效利用有关。过小的连接池可能导致连接不足,从而影响应用程序的性能。然而,过大的连接池可能会占用过多的系统资源,导致资源浪费。因此,需要根据应用程序的负载和系统资源情况来调整连接池的大小。
3. 数据库连接数限制: 数据库管理系统(DBMS)通常对同时连接到数据库的最大连接数有限制。连接池的大小不能超过数据库允许的最大连接数,否则会导致连接请求被拒绝或性能下降。
4. 并发访问控制: 连接池的大小还可以用于控制并发访问数据库的连接数。适当调整连接池的大小可以防止过多的并发连接对数据库造成过大的压力,从而提高系统的稳定性
线程设置最大线程数
内存溢出的情况
内存溢出(Memory Overflow)是指程序在运行时尝试申请更多内存空间,但无法满足需求,导致程序无法正常执行的情况。内存溢出通常是由于程序申请的内存超出了系统或进程的可用内存限制引起的。
什么情况下会发生栈内存溢出?
栈内存溢出(Stack Overflow)通常发生在程序的递归调用过程中,尤其是递归调用没有明确的终止条件或者终止条件不容易达到的情况下。栈内存主要用于保存方法的调用栈和局部变量,每个方法调用都会在栈上分配一段内存,如果递归调用层次太深,栈空间会被耗尽,从而导致栈内存溢出。
无限递归: 当一个方法无限递归调用自身,而且没有明确的终止条件时,栈内存就会迅速耗尽。
递归深度过大: 即使有明确的终止条件,但递归调用的层次过大,也可能导致栈内存溢出。
数据库查看当前线程数的命令
springIOC容器创建的过程
ioc加载机制
建立索引,邮箱查询唯一
数组跟列表的区别
链表节点的删除操作通常包括以下几个步骤:
1. 定位要删除的节点: 在链表中找到要删除的节点,通常需要从链表的头节点开始遍历,直到找到目标节点。
2. 调整前后节点的指针: 将目标节点的前一个节点的next指针指向目标节点的下一个节点,跳过目标节点,从而将目标节点从链表中删除。
3. 释放内存(可选): 如果使用动态内存分配创建节点,可以考虑释放目标节点所占用的内存。
B树和B+树的区别
B 树的所有节点既存放键(key) 也存放数据(data);而B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。
B 树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
B 树的检索过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点也可以做顺序检索。
数据库的回表查询
HashMap
1、底层数据结构是什么?
在 JDK 1.7 中,HashMap 底层使用数组+链表的形式进行存储,在 JDK 1.8 之后,增加了红黑树,即数组+链表+红黑树的形式存储元素。
其中数组是用来存储元素,链表是用来解决哈希冲突,而红黑树是用来提高查询效率的。
PS 可能会被问到解决 Hash 冲突的常见方法?
• 开放定址法:
线性探测法
二次探测法
• 链地址法
• 再哈希法
• 建立公共溢出区
2、链表和红黑树之间的转换?
• 链表长度大于 8 且数组长度大于 64,则将链表转换成红黑树;
• 链表长度小于 6 时会将红黑树转换成链表。
为何使用红黑树而非二叉树或平衡树?
相比普通二叉树,红黑树是一棵平衡树,它的添加、删除和查找操作最差时间复杂度为 O(logn),避免了普通二叉树最差情况下 O(n) 的复杂度。平衡二叉树是比红黑树更加严格的平衡树,为了达到平衡需要进行更多的旋转次数,所以红黑树插入删除操作效率更高。
HashMap为何负载因子是 0.75?(时间与空间转换)
1)如果我们设的负载因子比较大,元素比较多时,扩容时数组发生碰撞的位置较多,增加查找时间成本。
2)如果设置的比较小,元素又比较少时,数组还有足够空位的时候就发生了扩容,发生哈希碰撞的概率就降低了,查找时间成本降低,但是就需要更多的空间去存储元素,空间成本就增加了。
HashMap 是非线程安全的,可以使用 Java 中线程安全的Map,如 HashTable(数组+链表)
redis的缓存雪崩怎么处理
雪崩出现的原因
1. Redis 挂掉了,请求全部走数据库。
2. 对缓存数据设置相同的过期时间,导致某段时间内缓存失效,请求全部走数据库。
缓存雪崩如果发生了,很可能就把我们的数据库搞垮,导致整个服务瘫痪!
解决方案
缓存挂了的情况
1. 事发前:实现 Redis 的高可用(主从架构+Sentinel 或者 Redis Cluster),尽量避免 Redis 挂掉这种情况发生。
2. 事发中:万一 Redis 真的挂了,我们可以设置本地缓存(ehcache)+限流(hystrix),尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)
3. 事发后:redis 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
过期时间情况
在缓存的时候给过期时间加上一个随机值,这样就会大幅度的减少缓存在同一时间过期。
redis数据类型
过期时间
注意事项:
过期时间的设置和查询都是以秒为单位的整数。
过期时间是针对键的,而不是键的值。
过期时间的精度是秒,不支持毫秒级别的过期时间。
过期时间设置后,如果在此期间对键进行了更新操作,过期时间会重置。
同步锁和lock
lock锁的重用锁
资源对象(两个属性)
cas函数有几个参数,如何保证原子性
在系统中在各个层次如何保证高并发
秒杀,同一订单多线程保证数量正确
mysq索引最左匹配原则
MySQL 索引最左匹配原则是指,在使用联合索引(Composite Index,也叫复合索引)时,查询条件必须从联合索引的最左边的列开始,并且依次向右,才能充分利用这个索引。
这是因为 MySQL 索引的最左匹配原则,在查询时只能利用索引的最左侧的列,如果索引的前缀部分未被使用,那么查询性能可能无法得到优化。
mybatis中# {}的区别
1. #{}:预编译参数:#{} 是 MyBatis 中用于预编译参数的语法。在 SQL 语句中,#{} 会被 MyBatis 替换为一个占位符,并使用预编译的方式将参数值传递到 SQL 中,以防止 SQL 注入攻击。
2. ${}:直接拼接参数值:${} 是 MyBatis 中用于直接拼接参数值的语法。在 SQL 语句中,${} 会被 MyBatis 替换为实际的参数值,并将其直接拼接到 SQL 中。这样的处理方式存在 SQL 注入的风险,因为参数值可能直接影响 SQL 语句的结构。
• #{}:用于预编译参数,可以防止 SQL 注入。
• ${}:直接拼接参数值,存在 SQL 注入的风险,不建议在动态 SQL 中使用
最近最少使用回收算法
#面经##应届生##秋招##成都##java#