Java-高级篇
HashMap底层源码
JDK1.7 put方法
调用put方法时,会先检查table是否为空
- 如果为
null,初始化table(长度为16或者指定长度的2的幂次方)
- 如果为
table不为
null,判断key是否为null- 如果为
null,存放在index为0的位置
- 如果为
key不为
null,调用hash方法,将hash值与tbale.length-1进行与运算,计算出table下标,判断table[index]是否为null- 如果为
null,直接放入
- 如果为
table[index]不为null,判断key是否相同- 如果key相同,覆盖value值
key不同,调用添加方法
add方法判断
size >= 阈值 && table[bucketIndex] != null- 执行扩容方法
resize
- 执行扩容方法
最后调用添加方法
JVM 内存分哪几个区,每个区的作用是什么
JVM内存主要分为线程私有区,和线程共享区
线程共享区
- 堆:线程共享的一块区域,几乎所有对象实例都在这里创建,因此会发生GC
- 方法区:用于存储已被虚拟机加载的类的信息,常量,静态变量,也就是编译器编译后的代码等数据,Java8之前方法区叫永久代,Java8以后移除了方法区,将方法区移至元空间。
线程私有区
- 栈:每个方法在执行时都会创建一个栈帧并入栈,这个栈帧中存储了该方法的局部变量、操作数栈、动态链接信息以及方法返回地址等数据。
- 本地方法栈:本地方法栈和栈类似,只不过本地方法栈为 Native 方法服务。
- 程序计数器 :存储当前线程所执行的字节码指令的地址,即下一条即将被执行的指令所在的位置,是唯一 一个 java 虚拟机规范没有规定任何 OOM 情况的区域。
Java 中垃圾收集的方法有哪些
- 复制算法
- 标记-清除算法
- 标记-整理算法
- 分带算法
如何判断一个对象是否存活
引用计数器
每当有一个地方引用当前对象计数器+1,引用失效时-1,为0时说明没有引用,将会被垃圾回收;可能存在循环引用,无法回收问题,
可达性算法(引用链)
该对象就是通过一个引用链(GC Roots)对象作为起点,从这些节点向下搜索,走过的路径称为引用链,当当一个对象到GC Roots 没有任何引用链相连时,则对象不可用
GC Roots 对象主要有以下几种
- 虚拟机栈中的引用的对象
- 方法区静态属性医用的对象
- 方法区常量池引用的对象
- 本地方法栈JNI引用的对象
什么情况下会产生StackOverflowError(栈溢出)和OutOfMemoryError(堆溢出)
StackOverFlowError (栈溢出)
- 限递归循环调用(最常见)
- 执行了大量方法,导致线程栈空间耗尽
- 方法内声明了海量的局部变量
OutOfMemoryError(堆溢出)
- 内存中加载的数据量过于庞大,如一次从数据库取出过多数据
- 集合类中有对,对象的引用,使用完后未清空,使得JVM不能回收
- 代码中存在死循环或循环产生过多重复的对象实体
- 启动参数内存值设定的过小
什么是线程池,线程池有哪些(创建)
线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用 new 线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率
创建线程的方式
缓存线程池
- ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); 固定线程池
- ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4); 周期性线程池
- ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(4); 单例线程池
- ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
线程池底层工作原理

提交任务,判断核心线程数是否已满,
- 未满,创建线程执行任务
满了,判断队列是否已满,
- 未满,进入队列等待
满了,判断最大线程数是否满
- 未满,创建线程执行任务
满了,执行拒绝策略
线程池状态
- RUNNING(运行中):线程池处于正常运行状态,可以接受新任务并处理已提交的任务。
- SHUTDOWN(关闭中):线程池不再接受新任务,但会继续处理已提交的任务,直到任务队列为空。
shutdown()方法用于将线程池状态切换为SHUTDOWN。- STOP(停止中):线程池不再接受新任务,并且会尝试终止正在执行的任务。已提交但未执行的任务会从队列中移除。
shutdownNow()方法用于将线程池状态切换为STOP。- TIDYING(整理中):线程池在SHUTDOWN或STOP状态下,当所有任务都已经终止,工作线程数为0时,会将线程池状态切换为TIDYING,表示线程池正在进行一些清理工作。
- TERMINATED(终止):线程池的终止状态,表示线程池已经完全终止,不再处理任务。线程池状态会在TIDYING状态结束后切换到TERMINATED。
ThreadPoolExecutor对象有哪些参数 怎么设定核心线程数和最大线程数 拒绝策略有哪些
ThreadPoolExecutor 对象参数
corePoolSize 核心线程数
maximumPoolSize 最大线程数
keepAliveTime 存活时间
unit 单位
workQueue 任务队列
ArrayBlockingQueue 有界队列
LinkedBlockingDeque 无界队列
SynchronousQueue 同步队列
threadFactory 线程工厂
RejectedExecutionHandler 拒绝策略
队列容量计算
tasks :每秒的任务数,假设为500~1000
taskcost:每个任务花费时间,假设为0.1s
responsetime:系统允许容忍的最大响应时间,假设为1s
queueCapacity = ( coreSizePool / taskcost ) * responsetime
拒绝策略
- AbortPolicy 直接抛出异常,默认策略
- CallerRunsPolicy 用调用者所在的线程来执行任务;
- DiscardOldestPolicy 丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy 直接丢弃任务;当然也可以根据应用
核心线程数和最大线程数
CPU密集型 2*N
IO密集型 N+1
常见线程安全的并发容器有哪些
CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap、HashTable、Vector
Collections.synchronizedList(new ArrayList<>());
Collections.synchronizedMap(new HashMap<>());
Atomic原子类了解多少 原理是什么
类的加载过程
您的描述基本正确,不过在“准备”和“初始化”阶段的解释可以稍微详细一些:
加载:将.class文件(或其他数据源转换后的字节流)读入到JVM中,并创建一个java.lang.Class对象。这个Class对象包含了类的类型信息、方法数据、字段数据等。
验证:检查载入的字节码文件是否符合Java虚拟机规范,包括文件格式验证、元数据验证、字节码验证和符号引用验证等步骤,确保被加载的类是合法、安全且可执行的。
准备:为类的静态变量分配内存,并将其初始化为默认初始值(例如:整型变量初始化为0,布尔型为false,引用类型为null)。注意这里不包括实例变量,也不执行任何Java代码。
解析:将常量池中的符号引用替换为直接引用的过程。简单来说,就是将字符串形式的类名、方法名、字段名等转换为实际的内存地址。
初始化:真正为类变量赋予程序员指定的初始值,即执行类构造器
<clinit>方法。该方法由编译器自动收集类中所有静态变量的赋值动作和静态语句块合并而成。使用:完成以上步骤后,类就可以被JVM正常使用了,比如创建类的实例、调用类的方法等。
卸载:当满足一定条件时(如类不再有任何活跃的引用),类加载器可能会卸载对应的类,释放其占用的内存资源。但在实际应用中,类卸载的情况相对较少见,因为大多数情况下类会持续存在直到应用程序结束运行。