面试官的问题:
1、问介绍一下自己,以及技术栈
redis,java,简单的 python.数据库主要 mysql,次 oracle
2、redis 基础数据结构有哪些
key String,hash,LinkedList,set(集合),zset(有序集合)
3、mq 的作用
message queue
本质是对消息异步处理,将部分没那么重要的功能从主功能中整理出来。比如日志功能
解耦,将这些消息功能提取出来后,类似发邮件,发警告等消息提醒,可以从 mq 直接获取,使得系统更加的灵活
消峰。通过消息的排列处理,避免热点数据压垮系统的情况
缺点:增加了系统复杂性,系统的可用性降低(复杂后的链路变长,容易出问题)
常见的 kafka,rocketmq,activemq
4、如何解决迸发操作,例如同一账号同一时间不同设备同时点击领取了一张优惠券
通过 redis 缓存处理。
比如商品和相关的优惠券, 对应的规则关系,甚至是用户关系,都可以提前写到 redis 中缓存起来。 通过提前将算法结果保存起来, 在执行的时候,只需要查找,而不是实施的进行匹配
5、自动化日常如何执行的。一次多长时间如何缩短时间
一般情况使用 jenkins 自动跑。代码提交流程 xxxxxx
缩短自动化时间两种方法,一种是使用多线程进行测试验证,还有一种是服务切割。比如我本次提交了 200 个用例,那么可以通过设置 suit.xml 配置文件指定本次验证服务目录进行验证
6、jmeter 压测如何保障 tps 始终在一个数值
在负载逐渐升高的情况下,tps 却长期不变。这并不是说明性能很稳定,而是说明我们单位时间内的单线程 tps 是在逐渐降低的(单位时间 tps/总线程)。
再分析响应时间,我们的响应时间其实也是在逐渐升高,从侧面反映出线程的 tps 是在下降的。
7、压测内存过高如何解决
通过 top 命令,可以确认下,到底是哪个进程导致 CPU 变高,top
使用 top -Hp pid 来对该进程下的线程进行观察。展示的 pid 就是对应的线程了。
jstack 查找这个线程的信息
jstack [进程]|grep -A 10 [线程的 16 进制]
常见线程描述:
wait on monitor entry: 被阻塞的,这种情况是有问题的
runnable : 注意 IO 线程
in Object.wait(): 注意非线程池等待
8、spring 中怎么运用数据库? 2、spring IOC 的理解? 3、spring bean 的理解和使用? 4、支付回调延迟时,有什么方案可以去做监控?从代码上面怎么去实现?
引入 7 个包,配置下 xml 文件,当前更流行 springboot 使用 yarml 配置
控制反转:一般情况是纵向流程,耦合性较高。使用 spring 的控制反转,我们前端 jsp 或者 js 调用 spring 配置文件,通过每个 id 找到 bean,进而实现服务调用
依赖:通过容器对设置 bean,并配置响应 id 等参数;一般容器初始化或者首次调用 getbean 时触发; 通过 setbean 的形式实现参数注入
注入:构造器注入,通过 xml 设置 property 实现;通过 set 注入,通过 setbean 实现
支付回调延迟,分两类
第一类,要求不高时,可以第二天凌晨跑任务调用接口去统一下这类数据
第二类,要求较高,针对单个查询,主动调用支付端的查询支付结果,并反馈到页面上
针对其监控,可以对一个订单下单后付款超时结果进行设置,在判定超时前主动调用一次支付结果查询接口。如果有更新,那么直接更新;如果没有更新,抛异常;
9、索引数据结构介绍,和 B+ 树区别
数据库索引,是数据库管理系统中一个排序的数据结构,主要有
B 树索引、Hash 索引两种
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似 B+ 树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
哈希索引缺点 Hash 索引只支持等值比较, Hash 索引无法被用来避免数据的排序操作,Hash 索引不支持多列联合索引的最左匹配规则,Hash 索引在任何时候都不能避免表扫描
mysql 中用的最多是 B+ 树,
B+ 树的特征:
1.有 k 个子树的中间节点包含有 k 个元素(B 树中是 k-1 个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
2.所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
3.所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
B+ 树的优势:
1.单一节点存储更多的元素,使得查询的 IO 次数更少。
2.所有查询都要查找到叶子节点,查询性能稳定。
3.所有叶子节点形成有序链表,便于范围查询。
10、什么情况下可以不回表查询
回表查询 ,需要扫码两遍索引树 ,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。
怎么避免? 不是必须的字段就不要出现在 SELECT 里面。或者 b,c 建联合索引。但具体情况要具体分析,索引字段多了,存储和插入数据时的消耗会更大。这是个平衡问题。
10、MySQL 事务隔离级别
SQL 标准定义了四个隔离级别: 读取未提交,读取已提交,可重复读,可串行化
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
隔离级别 脏读 不可重复读 幻影读
READ-UNCOMMITTED √ √ √
READ-COMMITTED × √ √
REPEATABLE-READ × × √
SERIALIZABLE × × ×
11、为什么选择 Redis 做缓存
由于 redis 访问速度块、支持的数据类型比较丰富,所以 redis 很适合用来存储数据,另外结合 expire,我们可以设置过期时间然后再进行缓存更新操作
12、数据库和缓存的数据一致性怎么保证
正常的缓存步骤是:
1、查询缓存数据是否存在
2、不存在即查询数据库
3、将数据添加到缓存同时返回结果,
4、下一次访问发现缓存存在即直接返回缓存数据。
先淘汰后写数据库 vs 先写数据库后淘汰
先写后淘汰,如果淘汰失败,cache 里一直是脏数据
先淘汰后写,下次请求的时候缓存就会 miss hit 一次,这个代价是可以忽略的,(如果淘汰失败 return false)
缓存淘汰级制保证一致性.先淘汰缓存再写数据库,下次请求直接从数据库取然后再写在缓存里。
13、Redis 为什么是单线程的
单线程编程容易并且更容易维护;
Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
虽然说 Redis 是单线程模型,但是, 实际上,Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。
引入多线程主要是为了提高网络 IO 读写性能
14、Redis 持久化策略
Redis 的一种持久化方式叫快照(snapshotting,RDB),另一种方式是只追加文件(append-only file, AOF)
15、Redis 集群模式
为了避免单台服务器故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此, Redis 提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。
在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库 (slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
哨兵模式
第一种主从同步/复制的模式,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例。
16、哨兵挂了怎么办(所以哨兵应该也是集群)
哨兵是 redis 集群架构中非常重要的一个组件,主要功能如下:
集群监控:负责监控 redis master 和 slave 进程是否正常工作
消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员
故障转移:如果 master node 挂掉了,会自动转移到 slave node 上
配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址
哨兵的核心知识
故障转移时,判断一个 master node 是宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题
哨兵至少需要 3 个实例,来保证自己的健壮性
哨兵 + redis 主从的部署架构,是不会保证数据零丢失的,只能保证 redis 集群的高可用性
即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身就是单点,那么就不靠谱。
17、Redis 集群选举 master 过程(这个不会,说了 MySQL 集群的选主过程)
当 slave 发现自己的 master 变为 FAIL 状态时,便尝试进行 Failover,以期成为新的 master。由于挂掉的 master 可能会有多个 slave,从而存在多个 slave 竞争成为 master 节点的过程, 其过程如下:
1.slave 发现自己的 master 变为 FAIL
2.将自己记录的集群 currentEpoch 加 1,并广播 FAILOVER_AUTH_REQUEST 信息
3.其他节点收到该信息,只有 master 响应,判断请求者的合法性,并发送 FAILOVER_AUTH_ACK,对每一个 epoch 只发送一次 ack
4.尝试 failover 的 slave 收集 FAILOVER_AUTH_ACK
5.超过半数后变成新 Master
6.广播 Pong 通知其他集群节点。
18、MQ 是怎么防止消息丢失的
消息丢失分为三种情况 生产者丢失 mq 自己丢失 消费则丢失
消息确认机制
A:生产者丢失消息
①:可以选择使用 rabbitmq 提供是事务功能,就是生产者在发送数据之前开启事务,然后发送消息,如果消息没有成功被 rabbitmq 接收到,那么生产者会受到异常报错,这时就可以回滚事物,然后尝试重新发送;如果收到了消息,那么就可以提交事务。
缺点:rabbitmq 事物已开启,就会变为同步阻塞操作,生产者会阻塞等待是否发送成功,太耗性能会造成吞吐量的下降。
②:可以开启 confirm 模式。在生产者哪里设置开启了 confirm 模式之后,每次写的消息都会分配一个唯一的 id,然后如何写入了 rabbitmq 之中,rabbitmq 会给你回传一个 ack 消息,告诉你这个消息发送 OK 了;如果 rabbitmq 没能处理这个消息,会回调你一个 nack 接口,告诉你这个消息失败了,你可以进行重试。
B:rabbitmq 自己弄丢了数据
设置消息持久化到磁盘。设置持久化有两个步骤:
①创建 queue 的时候将其设置为持久化的,这样就可以保证 rabbitmq 持久化 queue 的元数据,但是不会持久化 queue 里面的数据。
②发送消息的时候讲消息的 deliveryMode 设置为 2,这样消息就会被设为持久化方式,此时 rabbitmq 就会将消息持久化到磁盘上。
必须要同时开启这两个才可以。
而且持久化可以跟生产的 confirm 机制配合起来,只有消息持久化到了磁盘之后,才会通知生产者 ack,这样就算是在持久化之前 rabbitmq 挂了,数据丢了,生产者收不到 ack 回调也会进行消息重发。
C:消费者弄丢了数据
使用 rabbitmq 提供的 ack 机制,首先关闭 rabbitmq 的自动 ack,然后每次在确保处理完这个消息之后,在代码里手动调用 ack。这样就可以避免消息还没有处理完就 ack。
19、介绍一下线程池,线程池的线程数量取值应该怎么取,有使用过哪些线程池
线程数量取值注意:当前的秒级 tps,单个线程能够支撑的 tps 量,两者结合相除取整,有小数则 +1
线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
降低资源消耗。
提高响应速度。
提高线程的可管理性。
线程数取值有两个方面:
a.CPU 密集型
第一种是 CPU 密集型任务,比如加密、解密、压缩、计算等一系列需要大量耗费 CPU 资源的任务。
最佳线程数 = CPU 核心数的 1~2 倍
b.IO 密集型
第二种任务是耗时 IO 型,比如数据库、文件的读写,网络通信等任务,这种任务的特点是并不会特别消耗 CPU 资源,但是 IO 操作很耗时,总体会占用比较多的时间。对于这种情况任务最大线程数一般会大于 CPU 核心数很多倍
c.通用公式
线程数 = CPU 核心数 * (1+ IO 耗时/CPU 耗时)
目前用的最多的是 ThreadPoolExecutor 线程池
分三种,fixthreadpoll,schedulethreadpool,cachethreadpool
20、如果使用无界等待队列会有什么问题
当线程在执行任务时需要调用远程服务,当调用远程服务异常时,就会导致线程处理每个任务都需要等待很长的时间;
处理任务的速度慢,产生任务的速度快,而任务队列是没有边界的,就会导致队列变得越来越大,从而导致内存飙升,还可能导致 OOM 内存溢出。
21、介绍一下锁
锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源
1、同步锁
同一时刻,一个同步锁只能被一个线程访问。以对象为依据,通过 synchronized 关键字来进行同步,实现对竞争资源的互斥访问。
2、独占锁(可重入的互斥锁)
互斥,即在同一时间点,只能被一个线程持有;可重入,即可以被单个线程多次获取。什么意思呢?根据锁的获取机制,它分为 “公平锁” 和 “非公平锁”。Java 中通过 ReentrantLock 实现独占锁,默认为非公平锁。
3、公平锁
是按照通过 CLH 等待线程按照先来先得的规则,线程依次排队,公平的获取锁,是独占锁的一种。Java 中,ReetrantLock 中有一个 Sync 类型的成员变量 sync,它的实例为 FairSync 类型的时候,ReetrantLock 为公平锁。设置 sync 为 FairSync 类型,只需——Lock lock = new ReetrantLock(true)。
4、非公平锁
是当线程要获取锁时,它会无视 CLH 等待队列而直接获取锁。ReetrantLock 默认为非公平锁,或——Lock lock = new ReetrantLock(false)。
5、共享锁
能被多个线程同时获取、共享的锁。即多个线程都可以获取该锁,对该锁对象进行处理。典型的就是读锁——ReentrantReadWriteLock.ReadLock。即多个线程都可以读它,而且不影响其他线程对它的读,但是大家都不能修改它。CyclicBarrier, CountDownLatch 和 Semaphore 也都是共享锁。
6、读写锁
维护了一对相关的锁,“读取锁” 用于只读操作,它是 “共享锁”,能同时被多个线程获取。“写入锁” 用于写入操作,它是 “独占锁”,只能被一个线程锁获取。
22、介绍一下锁升级过程
synchronized 锁的四种状态是在 jdk1.6 之后引入的,分别为:(无锁->偏向锁->轻量级锁->重量级锁 ) 这几个状态会随着竞争情况逐渐升级。
a:无锁
无锁的特点就是修改操作在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。也就是 CAS(CAS 是基于无锁机制实现的)。
b.偏向锁
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。
偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。
c.轻量级锁
是指当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。
若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。
d.重量级锁
在轻量级锁状态下,如果有第三个来访时,就会自动升级成重量级锁
23、介绍一下 ReEntrantLock 底层实现,介绍一下 AQS****************************************************************************
ReentrantLock 的底层实现机制是 AQS(Abstract Queued Synchronizer 抽象队列同步器)。AQS 没有锁之类的概念,它有个 state 变量,是个 int 类型,为了好理解,可以把 state 当成锁,AQS 围绕 state 提供两种基本操作 “获取” 和 “释放”,有条双向队列存放阻塞的等待线程。AQS 的功能可以分为独占和共享,ReentrantLock 实现了独占功能(每次只能有一个线程能持有锁)。
AQS:AbstractQuenedSynchronizer 抽象的队列式同步器。是除了 java 自带的 synchronized 关键字之外的锁机制。
AQS 的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
实现了 AQS 的锁有:自旋锁、互斥锁、读锁写锁、条件产量、信号量、栅栏
Bean 的生成过程
加载配置文件,将配置文件转换成 Documebt。转换成 BeanDefinition 然后转换成 BeanWraper,通过反射或 *** 生成实例,调用拦截器,再放入 BeanFactory 中
怎么样在 Bean 初始化完成后立即执行,而不是手动调方法
1:在 Spring 的配置文件中,添加注入:
2: 注解方式
使用注解只需要在方法名上添加@PostConstruct。
linux 怎么查看磁盘剩余多少
使用 df 命令
24、怎么查找一个文件里的某一个字符串的位置
Pattern 和 Matcher 。编译子串,创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配
25、HashMap,源码级别的问了,包括为什么线程不安全!!!!!!!!!!!
HashMap 底层是链表模式存储的。当链表复杂度达到一定层次后,会采用红黑树的形式取数。一个 key 过来后,
先根据 key 的 hashcode 对比 treenode 获取位置,之后再进行取数。
因为 hashmap 是异步操作,所以线程不安全
红黑树的好处就是可以切换。通过旋转或者变色维持树的平衡性
26、死锁
评注:这问题一面的时候问过了,嗯,凸显!
回答: 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。
产生死锁的原因主要是:
因为系统资源不足。
进程运行推进的顺序不合适。
资源分配不当等。
27、Synchronized 和 ReentrantLock 锁机制,怎么判断重入锁的,会不会是死锁?
评注:并发基础问题,懂并发编程的,应该都会。
回答:
先答区别:
API 方面:synchronized 既可以修饰方法,也可以修饰代码块。ReentrantLock 只能在方法体中使用。
公平锁:synchronized 的锁是非公平锁,ReentrantLock 默认情况下也是非公平锁,但可以通过带布尔值的构造函数要求使用公平锁。
等待可中断:假如业务代码中有两个线程,Thread1 Thread2。假设 Thread1 获取了对象 object 的锁,Thread2 将等待 Thread1 释放 object 的锁。
使用 synchronized。如果 Thread1 不释放,Thread2 将一直等待,不能被中断。synchronized 也可以说是 Java 提供的原子性内置锁机制。内部锁扮演了互斥锁(mutual exclusion lock ,mutex)的角色,一个线程引用锁的时候,别的线程阻塞等待。
使用 ReentrantLock。如果 Thread1 不释放,Thread2 等待了很长时间以后,可以中断等待,转而去做别的事情。
至于判断重入锁,ReenTrantLock 的字面意思就是再进入的锁,其实 synchronized 关键字所使用的锁也是可重入的,两者关于这个的区别不大。两者都是同一个线程没进入一次,锁的计数器都自增 1,所以要等到锁的计数器下降为 0 时才能释放锁。
28、进程和线程的区别?
评注:操作系统的题目,很常规!
回答:
进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。
一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务。
线程是一种轻量级的进程,与进程相比,线程给操作系统带来侧创建、维护、和管理的负担要轻,意味着线程的代价或开销比较小。
29、进程之间如何保证同步?
临界区 适用同步方法,通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问
互斥量 为协调共同对一个共享资源的单独访问而设计的。
信号量 为控制一个具有有限数量用户资源而设计,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。互斥量是信号量的一种特殊情况,当信号量的最大资源数=1 就是互斥量了
事件 用来通知线程有一些事件已发生,从而启动后继任务的开始
针对临界值,四个规则:有空让进,无空等待,多个则一,让权等待,有限等待
30、分布式锁
评注:此题问的没头没尾的,分布式锁可以问的点很多,比如实现方式啊?性能差距啊?
回答:这题如果要详答,看我的另一篇文章《分布式之抉择分布式锁》
分布式锁有三种实现方式:数据库、缓存、Zookeeper,这里我直接罗列一下各种锁的对比吧
从理解的难易程度角度(从低到高)
数据库 > 缓存 > Zookeeper
从实现的复杂性角度(从低到高)
Zookeeper >= 缓存 > 数据库
从性能角度(从高到低)
缓存 > Zookeeper >= 数据库
从可靠性角度(从高到低)
Zookeeper > 缓存 > 数据库
31、对象 GC
java 堆,方法区,本地方法区,虚拟机栈,程序计数器
程序运行主要在 java 堆中,java 数组,对象存在区域。
方法区存放静态变量,常量等
垃圾回收算法:
清除算法,复制算法,压缩算法
虚拟机栈中引用的对象
方法区中类静态属性引用的对象
方法区中产量引用的对象
本地方法栈中 JNI 引用的对象
31、垃圾回收算法
评注:一面问过,把一面的回答贴过来!
回答:
标记 - 清除算法、标记整理算法、复制算法、压缩算法
32、JVM 参数
标准参数 - 举例:-jar,-verbose:gc 输出每次 gc 情况
非标准参数 -X 举例:-Xint,解释执行模式,所有的字节码将被直接执行,而不会编译成本地码;
非 stable 参是 -XX 举例:-XX:+PrintGCDetails 开启 GC 详细信息 -XX:MaxPermSize=64m 老生代对象能占用内存的最大值
33、OOM 出现的有哪些场景?为什么会发生?
评注:常规题,只是情况太多了!
回答:
OOM for Heap (java.lang.OutOfMemoryError: Java heap space):heap 的最大值不满足需要,将设置 heap 的最大值调高即可。
OOM for StackOverflowError (Exception in thread "main" java.lang.StackOverflowError):如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常。
OOM for GC (java.lang.OutOfMemoryError: GC overhead limit exceeded):此 OOM 是由于 JVM 在 GC 时,对象过多,导致内存溢出。
OOM for native thread created (java.lang.OutOfMemoryError: unable to create new native thread):这个异常问题本质原因是我们创建了太多的线程,而能创建的线程数是有限制的,导致了异常的发生。
OOM for allocate huge array (Exception in thread "main": java.lang.OutOfMemoryError: Requested array size exceeds VM limit):此类信息表明应用程序试图分配一个大于堆大小的数组。例如,如果应用程序 new 一个数组对象,大小为 512M,但是最大堆大小为 256M,因此 OutOfMemoryError 会抛出,因为数组的大小超过虚拟机的限制。
OOM for small swap (Exception in thread "main": java.lang.OutOfMemoryError: request bytes for . Out of swap space? ):抛出这类错误,是由于从 native 堆中分配内存失败,并且堆内存可能接近耗尽。
OOM for exhausted native memory (java.lang.OutOfMemoryErr java.io.FileInputStream.readBytes(Native Method)):由于 Native memory 被耗尽导致的。
34、JVM 内存结构说下吧
评注:常规题,学过 JVM 的都会!
回答:来一张图就好了。
有 5 大内存区域,按照是否被线程所共享可分为两部分,一部分是线程独占区域,包括 Java 栈,本地方法栈和程序计数器。还有一部分是被线程所共享的,包括方法区和堆。注意了,下一题就是问的是堆和栈的共享问题!
如下图所示
35、堆和栈的共享问题?
评注:常规题,大家应该都答的上来,属应届生常见问题!
回答:
栈内存的一个特点是数据共享,比如你线程中执行下面两句话
int i = 1;
int j = 1;
前面定义了 i=1,i 和 1 都在栈内存内,如果再定义一个 j=1,此时将 j 放入栈内存,然后查找栈内存中是否有 1,如果有则 j 指向 1。
堆内存没有数据共享的特点,比如你线程中执行了下面两句话
String s = new String( "Hello World" );
String w = new String( "Hello World" );
此时如下图所示
36、有比较过 Http 和 RPC 吗?
评注:此题我按我的理解来答。我不确定对不对,因此我觉得 http 和 rpc 没啥可比性,不是一个级别的概念。
回答:
只要是远程调用都可以叫 RPC,和是不是通过 http 没什么关系。
那么,调用过程,也就是通信过程之间需要协议,可以是 HTTP 协议、dubbo 协议等、其他协议等。
37、HttpClient 你说说里面的具体实现吧?(涉及了哪些东西)
评注:此题只能回答一个大概,我找了一个架构图来进行说明
回答:
如下图所示
Httpclient 将对接的服务器或者集群(相同域名)称为 route,并为每个 route 建立若干连接,并池化在连接池里。Client 通过 tcp/ip 协议发送请求以及接受应答,在发送请求前和接收应答后都会经由 interceptor 进行链式处理,在 httpclient 里这些 interceptor 被称为 HttpProcessor,负责处理诸如设置报文头,报文体,编码格式等以及解析报文头,报文体,解码格式等 http 规范报文格式范畴内的事情。
38、SpringMvc 工作原理?
39、Session Cookie 区别?
session 在服务器,cookie 在客户端,且大小必须<4k
40、浅拷贝,深拷贝
都是调用的 object 的 clone() 方法
浅拷贝,直接调用 clone,只复制当前对象,父级集成对象不复制,
深拷贝,完全复制,需要调用 super.clone 对对象进行重写
41、wait 和 sleep 的区别
wait 属于 object,sleep 属于 thread 的方法
当调用 sleep 时,锁不会被释放,可以理解为占着茅坑不拉屎。此时必须加入捕获异常
wait 被调用时,会放弃对象锁,程序可以正常进行。
42、线程池参数,整个流程描述
int corePoolSize(线程池基本大小),int maximumPoolSize(线程池最大大小),long keepAliveTime(线程活动保持时间),TimeUnit unit(时间单位),
BlockingQueue workQueue,ThreadFactory:用于设置创建线程的工厂
43、CAS 和 AQS 是啥,原理
CAS 比较和替换。比较原则类似乐观锁
AQS 队列同步器,主要用来构建锁和同步框架
ReentrantLock 独占锁
共享锁
44、ThreadLocal 原理,注意事项,参数传递
hashmap 的形式存储线程私有成员变量。
createMap(Thread t, T firstValue) 自定义一个 threadlocal 类,就是将 thread 作为一个 key,然后存储响应的 value
threadLocal 是线程的私有成员变量
注意事项:
脏数据,因为线程池线程公用,所以对应的 value 会积累脏数据
内存泄露,如果执行完线程不调用 remove 方法,这个对象不会被释放
45、servlet 是否线程安全,如何改造
不安全。
4 个原则:
1、禁止出现实例变量
2、使用单线程时安全
2、使用多线程时,对共享数据添加互斥锁。比如 reternlock。但是会大量降低性能
46、jvm 问题工具,jps,jinfo,jmap
jps -v,-l 等,用于查看当前代码中的类以及执行指令等信息
jstat 监视器相关查看
jinfo 打印配置信息
jmap 堆信息
jstack 输出线程相关信息
G1 收集器的内存划分怎么样的?
说下标记 - 清除算法的具体过程吧?
知道有哪些 jvm 参数么?说一下吧?
设定堆内存大小
-Xmx:堆内存最大限制。
设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代
-XX:NewSize:新生代大小
-XX:NewRatio 新生代和老生代占比
-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比
设定垃圾回收器 年轻代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC
有了解过 jvm 调优么? 用过什么命令? 一般怎么调优?
Mysql 主从复制是怎么工作的呢?说说各个线程具体做了什么吧?
jvm gc 的时候整个过程是怎么样的呢?
你在项目中一般怎么调优 JVM 的呢?