2022腾讯面试题
2**022【京东】面试真题:**
1**、哪些情况下的对象会被垃圾回收机制处理掉?**
利用可达性分析算法,虚拟机会将一些对象定义为GCRoots,从GCRoots出发沿着引用链 向下寻找,如果某个对象不能通过GCRoots寻找到,虚拟机就认为该对象可以被回收掉。
⚫ 哪些对象可以被看做是GCRoots呢?
1)虚拟机栈(栈帧中的本地变量表)中引用的对象; 2)方法区中的类静态属性引用的对象,常量引用的对象;
3)本地方法栈中JNI(Native方法)引用的对象;
⚫ 对象不可达,一定会被垃圾收集器回收么?
即使不可达,对象也不一定会被垃圾收集器回收,1)先判断对象是否有必要执行finalize()方法,对象必须重写finalize()方法且没有被运行过。2)若有必要执行,会把对象放到一个 队列中,JVM会开一个线程去回收它们,这是对象最后一次可以逃逸清理的机会。
2**、讲一下常见编码方式**?
编码的意义:计算机中存储的最小单元是一个字节即8bit,所能表示的字符范围是255个, 而人类要表示的符号太多,无法用一个字节来完全表示,固需要将符号编码,将各种语言翻 译成计算机能懂的语言。
⚫ ASCII码:总共128个,用一个字节的低7位表示,0〜31控制字符如换回车删除等; 32~126是打印字符,可通过键盘输入并显示出来;
⚫ ISO-8859-1,用来扩展ASCII编码,256个字符,涵盖了大多数西欧语言字符。
⚫ GB2312:双字节编码,总编码范围是A1-A7,A1-A9是符号区,包含682个字符,B0-B7 是 汉字区,包含6763个汉字;
⚫ GBK为了扩展GB2312,加入了更多的汉字,编码范围是8140~FEFE,有23940个码 位,能 表示21003个汉字。
⚫ UTF-16: ISO试图想创建一个全新的超语言字典,世界上所有语言都可通过这本字典 Unicode来相互翻译,而UTF-16定义了Unicode字符在计算机中存取方法,用两个字 节来表 示Unicode转化格式。不论什么字符都可用两字节表示,即16bit,固叫UTF- 16。
⚫ UTF-8:UTF-16统一采用两字节表示一个字符,但有些字符只用一个字节就可表示,浪 费存储空间,而UTF-8采用一种变长技术,每个编码区域有不同的字码长度。 不同类型 的 字 符 可 以 由1~6个字节组成。
3**、utf-8编码中的中文占几个字节;int 型几个字节**?
utf-8是一种变长编码技术,utf-8编码中的中文占用的字节不确定,可能2个、3个、4个,int型占4个字节。
4**、静态代理和动态代理的区别,什么场景使用?**
代理是一种常用的设计模式,目的是:为其他对象提供一个代理以控制对某个对象的访问, 将两个类的关系解耦。代理类和委托类都要实现相同的接口,因为代理真正调用的是委托类 的方法。
区别:
⚫ | 静态代理:由程序员创建或是由特定工具生成,在代码编译时就确定了被代理的类是哪 一 |
个是静态代理。静态代理通常只代理一个类;
⚫ 动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口 下 的多个实现类;
实现步骤:
a.实现InvocationHandler接口创建自己的调用处理器;
b.给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类; c.利用反射机制得到动态代理类的构造函数;
d.利用动态代理类的构造函数创建动态代理类对象;
使用场景:Retrofit中直接调用接口的方法;Spring的AOP机制;
5**、简述下Java 的异常体系**。
Java中Throwable是所有异常和错误的超类,两个直接子类是Error(错误)和Exception(异常):
⚫ Error是程序无法处理的错误,由JVM产生和抛出,如OOM、ThreadDeath等。这些 异常 发生时,JVM一般会选择终止程序。
⚫ Exception是程序本身可以处理的异常,又分为运行时异常(RuntimeException)(也叫 Checked Eception)和 非 运 行 时 异 常(不 检 查 异 常Unchecked Exception)。 运 行 时 异 常 有NullPointerException\IndexOutOfBoundsException等,这些异常一般是 由程序逻辑错误引起 的,应尽可能避免。非运行时异常有
IOException\SQLException\FileNotFoundException以及 由用户自定义的Exception 异常等。
6**、谈谈你对解析与分派的认识。**
解析指方法在运行前,即编译期间就可知的,有一个确定的版本,运行期间也不会改变。解析是静态的,在类加载的解析阶段就可将符号引用转变成直接引用。
分派可分为静态分派和动态分派,重载属于静态分派,覆盖属于动态分派。静态分派是指在重载时通过参数的静态类型而非实际类型作为判断依据,在编译阶段,编译器可根据参数的静态类型决定使用哪一个重载版本。动态分派则需要根据实际类型来调用相应的方法。
7**、修改对象 A的equals方法的签名,那么使用HashMap存放这个对象**实
例的时**候,会用哪个 equals方法?**
会调用对象对象的equals方法。
“==”如果是基本类型的话就是看他们的数据值是否相等就可以。 如果是引用类型的话,比较的是栈内存局部变量表中指向堆内存中的指针的值是否相等。 “equals”如果对象的equals方法没有重写的话,equals方法和“==”是同一种。hashcod是返回对象实例内存地址的hash映射。 理论上所有对象的hash映射都是不相同的。
8**、Java中实现多态的机制是什么**?
多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时不确定,在运行期间才确定,一个引用变量到底会指向哪个类的实例。这样就可以不用 修改源程序,就可以让引用变量绑定到各种不同的类实现上。Java实现多态有三个必要条件: 继承、重定、向上转型,在多态中需要将子类的引用赋值给父类对象,只有这样该引用才能 够具备调用父类方法和子类的方法。
9**、如何将一个Java 对象序列化到文件里?**
ObjectOutputStream.writeObject()负责将指定的流写入,ObjectInputStream.readObject()从指 定流读取序列化数据。
//写 入
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(“D:/student.txt”));
os.writeObject(studentList);
os.close();
} catch (FileNotFoundExceptione) {
e.printStackTrace();
} catch (IOExceptione) {
e.printStackTrace();
}
10**、说说你对Java反射的理解。**
在运行状态中,对任意一个类,都能知道这个类的所有属性和方法,对任意一个对象,都能 调用它的任意一个方法和属性。这种能动态获取信息及动态调用对象方法的功能称为java语言的反射机制。
反射的作用:开发过程中,经常会遇到某个类的某个成员变量、方法或属性是私有的,或只对系统应用开放,这里就可以利用java的反射机制通过反射来获取所需的私有成员或是方法。
⚫ ⚫ | 获取类的Class对象实例Classclz=Class.forName(“com.zhenai.api.Apple“); 根据Class对象实例获取Constructor对 象Constructor appConstructor = |
clz.getConstructor();
⚫ 使用Constructor对 象 的newInstance方 法 获 取 反 射 类 对 象Object appleObj = appConstructor.newInstance();
⚫ 获取方法的Method对象
MethodsetPriceMethod=clz.getMethod(“setPrice”,int.class);
⚫ 利用invoke方法调用方法setPriceMethod.invoke(appleObj,14);
⚫ 通过getFields()可以获取Class类的属性,但无法获取私有属性,而
getDeclaredFields()可 以获取到包括私有属性在内的所有属性。带有Declared修饰的方 法可以反射到私有的方法, 没有Declared修饰的只能用来反射公有的方法,其他如 Annotation\Field\Constructor也是如此。
11**、说说你对Java注解的理解。**
注解是通过@interface关键字来进行定义的,形式和接口差不多,只是前面多了一个@ public@interfaceTestAnnotation{
}
使用时@TestAnnotation来引用,要使注解能正常工作,还需要使用元注解,它是可以注解到注解上的注解。元标签有@Retention、@Documented、@Target、@Inherited和@Repeatable五种。
@Retention说明注解的存活时间,取值有RetentionPolicy.SOURCE注解只在源码阶段保留,在编译器进行编译时被丢弃;RetentionPolicy.CLASS注解只保留到编译进行的时候,并不会被加载到JVM中。RetentionPolicy.RUNTIME可以留到程序运行的时候,它会被加载进入到JVM中,所以在程序运行时可以获取到它们。
@Documented注解中的元素包含到javadoc中去。
@Target限定注解的应用场景,ElementType.FIELD给属性进行注解;
ElementType.LOCAL_VARIABLE可以给局部变量进行注解;ElementType.METHOD可以给方法进行注解;ElementType.PACKAGE可以给一个包进行注解ElementType.TYPE可以给一个类型进行注解,如类、接口、枚举。
@Inherited若一个超类被@Inherited注解过的注解进行注解,它的子类没有被任何注解应用 的话,该子类就可继承超类的注解;
注解的作用:
⚫ 提供信息给编译器:编译器可利用注解来探测错误和警告信息
⚫ 编译阶段:软件工具可以利用注解信息来生成代码、html文档或做其它相应处理;
⚫ 运行阶段:程序运行时可利用注解提取代码
注解是通过反射获取的,可以通过Class对象的isAnnotationPresent()方法判断它是否应用了某个注解,再通过getAnnotation()方法获取Annotation对象
12**、说一下泛型原理,并举例说**明。
泛型就是将类型变成参数传入,使得可以使用的类型多样化,从而实现解耦。Java泛型是在Java1.5以后出现的,为保持对以前版本的兼容,使用了擦除的方法实现泛型。擦除是指在 一定程度无视类型参数T,直接从T所在的类开始向上T的父类去擦除,如调用泛型方法, 传入类型参数T进入方法内部,若没在声明时做类似
publicTmethodName(TextendsFathert){ },Java就进行了向上类型的擦除,直接把参数t当做Object类来处理,而不是传进去的T。 即在有泛型的任何类和方法内部,它都无法知道自己的泛型参数,擦除和转型都是在边界上发生,即传进去的参在进入类或方法时被擦除掉,但传出来的时候又被转成了我们设置的T。在泛型类或方法内,任何涉及到具体类型(即擦除后的类型的子类)操作都不能进行,如newT(),或者T.play()(play为某子类的方法而不是擦除后的类的方法)。
13**、谈谈你对Java中String的了解**。
⚫ String类是final型,固String类不能被继承,它的成员方法也都默认为final方法。 String对象一旦创建就固定不变了,对String对象的任何改变都不影响到原对象,相关 的任何改变 操作都会生成新的String对象。
⚫ String类是通过char数组来保存字符串的,String对equals方法进行了重定,比较的 是 值相等。
String a=”test”;String b=”test”;String c=newString(“test”);
a、b和字面上的test都是指向JVM字符串常量池中的”test”对象,他们指向同一个对象。而new关键字一定会产生一个对象test,该对象存储在堆中。所以newString(“test”)产生了两个对象,保存在栈中的c和保存在堆中的test。而在java中根本就不存在两个完全一模一样的字符串对象,故在堆中的test应该是引用字符串常量池中的test。
例:
String str1=”abc”;//栈中开辟一块空间存放引用str1,str1指向池中String常量”abc”
String str2=”def”;//栈中开辟一块空间存放引用str2,str2指向池中String常量”def”
String str3=str1+str2;//栈中开辟一块空间存放引用str3//str1+str2通过StringBuilder的最后一步toString()方法返回一个新的String对象”abcdef”
//会在堆中开辟一块空间存放此对象,引用str3指向堆中的(str1+str2)所返回的新String对象。System.out.println(str3==”abcdef”);//返回false因为str3指向堆中的”abcdef”对象,而”abcdef”是字符池中的对象,所以结果为false。JVM对Stringstr=”abc”对象放在常量池是在编译时做的 , 而Stringstr3=str1+str2是在运行时才知道的,new对象也是在运行时才做的。
14**、String为什么要设计成不可变的**?
⚫ 字符串常量池需要String不可变。因为String设计成不可变,当创建一个String对象 时, 若此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的 对象。 如果字符串变量允许必变,会导致各种逻辑错误,如改变一个对象会影响到另一个 独立对象。
⚫ String对象可以缓存hashCode。字符串的不可变性保证了hash码的唯一性,因此可以 缓 存String的hashCode,这样不用每次去重新计算哈希码。在进行字符串比较时,可 以直接比较hashCode,提高了比较性能;
⚫ 安全性。String被许多java类用来当作参数,如url地址,文件path路径,反射机制所 需的Strign参数等,若String可变,将会引起各种安全隐患。
15**、Redis常见的几种数据结构说一下?各自的使用场景?**
st**ring**
介绍:string 数据结构是简单的 key-value 类型。
使用场景: 一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等。
li**s**t
介绍:li**s**t 即是 链**表**
使用场景:发布与订阅或者说消息队列、慢查询。
h**as**h
介绍:hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。 使用场景:系统中对象数据的存储。
se**t**
介绍:set 类似于 Java 中的 HashSet 。Redis 中的 set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作
使用场景: 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景。
so**rted se**t
介绍:和 set 相比,sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
使用场景:需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
bi**t**map
介绍:bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。。
使用场景:适合需要保存状态信息(比如是否签到、是否登录…)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。。
16**、谈一谈缓存穿透、缓存击穿和缓存雪崩,以及各自的解决方案?**
缓存穿透
⚫ 问题:大量并发查询不存在的KEY,在缓存和数据库中都不存在,同时给缓存和数据库带 来压力。
⚫ 原因:一般而言,缓存穿透有2种可能性:业务数据被误删,导致缓存和数据库中都没有 数据。恶意进行ddos 攻击。
⚫ 分析:为什么会多次透传呢?不存在 一直为空,需要注意让缓存能够区分KEY不存在和 查询到一个空值。
⚫ 解决办法:缓存空值的KEY,这样第一次不存在也会被加载会记录,下次拿到有这个 KEY。Bloom过滤或RoaingBitmap 判断KEY是否存在,如果布隆过滤器中没有查到这 个数据,就不去数据库中查。在处理请求前增加恶意请求检查,如果检测到是恶意攻击, 则拒绝进行服务。完全以缓存为准,使用延迟异步加载的策略(异步线程负责维护缓存的 数据,定期或根据条件触发更新),这样就不会触发更新。
缓存击穿
⚫ 问题:某个KEY失效的时候,正好有大量并发请求访问这个KEY。
⚫ 分析:跟穿透其实很像,属于比较偶然的。
⚫ 解决办法:KEY的更新操作添加全局互斥锁。完全以缓存为准,使用延迟异步加载的策略 (异步线程负责维护缓存的数据,定期或根据条件触发更新),这样就不会触发更新。
缓存雪崩
⚫ | 问题:当某一时刻发生大规模的缓存失效的情况,导致大量的请求无法获取数据,从而将 |
流量压力传导到数据库上,导致数据库压力过大甚至宕机。
⚫ 原因:一般而言,缓存雪崩有2种可能性:大量的数据同一个时间失效:比如业务关系强 相关的数据要求同时失效Redis 宕机
⚫ 分析:一般来说,由于更新策略、或者数据热点、缓存服务宕机等原因,可能会导致缓存 数据同一个时间点大规模不可用,或者都更新。所以,需要我们的更新策略要在时间上合 适,数据要均匀分享,缓存服务器要多台高可用。
⚫ 解决办法:更新策略在时间上做到比较平均。如果数据需要同一时间失效,可以给这批数 据加上一些随机值,使得这批数据不要在同一个时间过期,降低数据库的压力。使用的热 数据尽量分散到不同的机器上。多台机器做主从复制或者多副本,实现高可用。做好主从 的部署,当主节点挂掉后,能快速的使用从结点顶上。实现熔断限流机制,对系统进行负 载能力控制。对于非核心功能的业务,拒绝其请求,只允许核心功能业务访问数据库获取 数据。服务降价:提供默认返回值,或简单的提示信息。
17**、讲下Kafka、RabbitMQ、RocketMQ之间的区别是什么**?
性能
消息中间件的性能主要衡量吞吐量,Kafka的吞吐量比RabbitMQ要高出1~2个数量级,RabbitMQ的单机QPS在万级别,Kafka的单机QPS能够达到百万级别。RocketMQ单机写入TPS单实例约7万条/秒,单机部署3个Broker,可以跑到最高12万条/秒,消息大小10个字节,Kafka如果开启幂等、事务等功能,性能也会有所降低。
数据可靠性
Kafka与RabbitMQ都具备多副本机制,数据可靠性较高。RocketMQ支持异步实时刷盘,同步刷盘,同步Replication,异步Replication。
服务可用性
Kafka采用集群部署,分区与多副本的设计,使得单节点宕机对服务无影响,且支持消息容量的线性提升。RabbitMQ支持集群部署,集群节点数量有多种规格。RocketMQ是分布式架构,可用性高。
功能
Kafka与RabbitMQ都是比较主流的两款消息中间件,具备消息传递的基本功能,但在一些特殊的功能方面存在差异,RocketMQ在阿里集团内部有大量的应用在使用。
18**、Kafka的架构说一下**?
整个架构中包括三个角色。
⚫ 生产者(Producer):消息和数据生产者。
⚫ ⚫ | 代理(Broker):缓存代理,Kafka的核心功能。 消费者(Consumer):消息和数据消费者。 |
Kafka给Producer和Consumer提供注册的接口,数据从Producer发送到Broker,Broker承担一个中间缓存和分发的作用,负责分发注册到系统中的Consumer。
19**、Kafka怎么保证消息是有序的?**
消息在被追加到 Partition(分区)的时候都会分配一个特定的偏移量(offset)。Kafka 通过偏移量(offset)来保证消息在分区内的顺序性。发送消息的时候指定key/Partition。
20**、Kafka怎么保证消息不丢失**?
生产者丢失消息的情况
生产者(Producer) 调用send方法发送消息之后,消息可能因为网络问题并没有发送过去。 为了确定消息是发送成功,我们要判断消息发送的结果,Kafka 生产者(Producer) 使用 send 方法发送消息实际上是异步的操作,我们可以通过 get()方法获取调用结果,但是这样也让它变为了同步操作,可以采用为其添加回调函数的形式,示例代码如下:
ListenableFuture
ex -> logger.error(“生产者发送消失败,原因:{}“, ex.getMessage()));
P**roducer**的retries(重试次数)设置一个比较合理的值,一般**是 3 ,但是为了保证消息不丢失的话一般会设置比较大一点。设置完成之后,当出现网络问题之后能够自动重试消息发送,避免消息丢失。另外,建议还要设置重试间隔,因为间隔太小的话重试的效果就不明显了,网络波动一次你3**次一下子就重试完了
消费者丢失消息的情况
当消费者拉取到了分区的某个消息之后,消费者会自动提交了 offset。自动提交的话会有一个问题,试想一下,当消费者刚拿到这个消息准备进行真正消费的时候,突然挂掉了,消息实际上并没有被消费,但是 offset 却被自动提交了。
解决办法也比较粗暴,我们手动关闭自动提交**offset,每次在真正消费完消息之后再自己手动提交offset。** 但是,细心的朋友一定会发现,这样会带来消息被重新消费的问题。比如你刚刚消费完消息之后,还没提交 offset,结果自己挂掉了,那么这个消息理论上就会被消费两次。
K**afka弄丢了消息**
试想一种情况:假如 lea**der副本所在的broker突然挂掉,那么就要从 follower副本重新选出一个leader,但是 leader 的数据还有一些没有被 follower副本的同步的话,就会造成消息丢失。**
当我们配置了 u**nclean.leader.election.enable = fals**e 的话,当 leader 副本发生故障时就不会从 follower 副本中和 leader 同步程度达不到要求的副本中选择出 leader ,这样降低了消息丢失的可能性。
21**、Kafka怎么解决重复消费?**
⚫ 生产者发送每条数据的时候,里面加一个全局唯一的id,消费到了之后,先根据这个id 去比如Redis里查一下,之前消费过吗,如果没有消费过,就处理,然后这个id写 Redis。如果消费过就别处理了。
⚫ 基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据 插入只会报错,不会导致数据库中出现脏数据。
22**、介绍**下 M**ySQ**L 聚簇**索引与非聚簇索引的区别(InnoDB与 Myisa**m 引
擎)**?**
聚集索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表只能有一 个聚簇索引,因为一个表的物理顺序只有一种情况,所以,对应的聚簇索引只能有一个。 聚簇索引的叶子节点就是数据节点,既存储索引值,又在叶子节点存储行数据。
Innodb 创建表后生成的文件有:
frm:创建表的语句
idb:表里面的数据+索引文件
非聚集索引(MyISAM 引擎的底层实现)的逻辑顺序与磁盘上行的物理存储顺序不同。非聚簇 索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针。索引命中后,需要回表查 询。
Myisam 创建表后生成的文件有:
frm:创建表的语句MYD:表里面的数据文件(myisam data) MYI:表里面的索引文件(myisam index)
innodb的次索引指向对主键的引用 (聚簇索引)
myisam的次索引和主索引都指向物理行 (非聚簇索引)
23**、然后给一个联合索引(a,b)和一个语句,select * from tablewhere b**=
‘x**xx’,判断是否能命中索引?为什么**?
不能命中。
对于查询SELECT * FROM TABLE WHERE a=xxx and b=xxx,显然是可以使用(a,b)这个联合索引的。
对于单个的a列查询SELECT * FROM TABLEWHERE a=xxx,也可以使用这个(a,b)索引。
但对于b列的查询SELECT *FROM TABLE WHERE b=xxx,则不可以使用这棵B+树索引。 在innoDb 数据引擎中,可以发现叶子节点上的b值为1、2、1、4、1、2,显然不是排序的,因此对于b列的查询使用不到(a,b)的索引
24**、Java多线程有哪几种实现方式**?
⚫ 通过继承Thread类创建线程类
⚫ 实现Runnable接口创建线程类
⚫ 通过Callable和Future接口创建线程
25**、用过ConcurrentHashMap,讲一下他**和 H**ashTable的不同之处**?
⚫ HashTable就是实现了HashMap加上了synchronized,而ConcurrentHashMap底层 采用分段的数组+链表实现,线程安全
⚫ ConcurrentHashMap通过把整个Map分为N个Segment,可以提供相同的线程安 全,但是效率提升N倍,默认提升16倍。
⚫ 并且读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新 的值。
⚫ Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占, ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
⚫ 扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个 Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容
26**、Java怎么实现线程安全**?
⚫ 使用同步代码块
⚫ 使用同步方法
⚫ 使用Lock锁机制, 通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定 的代码块
27**、描述ThreadLocal(线程本地变量)的底层实现原理及常用场景。**
实现原理:
⚫ 每个Thread线程内部都有一个ThreadLocalMap;以线程作为key,泛型作为value,可 以理解为线程级别的缓存。每一个线程都会获得一个单独的map。
⚫ 提供了set和get等访问方法,这些方法为每个使用该变量的线程都存有一份独立的副 本,因此get方法总是返回由当前执行线程在调用set时设置的最新值。
应用场景:
⚫ JDBC连接
⚫ Session管理
⚫ Spring事务管理
⚫ 调用链,参数传递
⚫ AOP
ThreadLocal是一个解决线程并发问题的一个类,用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。例如,由于JDBC的连接对象不是线程安全的,因此,当多线程应用程序在没有协同的情况下,使用全局变量时,就不是线程安全的。通过将JDBC的连接对象保存到ThreadLocal中,每 个线程都会拥有属于自己的连接对象副本。
28**、介绍**下 Sp**ring Bean 都有哪些作用域**?
⚫ 单例singleton : bean在每个Spring IOC容器中只有一个实例。
⚫ 原型prototype:一个bean的定义可以有多个实例。
⚫ request:每次http请求都会创建一个bean。
⚫ session:在一个HTTP Session中,一个bean定义对应一个实例。
⚫ globalsession
⚫ application
29**、注解@Autowired和@Resource有什么区**别?
⚫ Resource是JDK提供的,而Autowired是Spring提供的
⚫ Resource不允许找不到bean的情况,而Autowired允许(@Autowired(required = false))
⚫ 指定name的方式不一样,@Resource(name =
“baseDao”),@Autowired()@Qualifier(“baseDao”)
Resource默认通过name查找,而Autowired默认通过type查找
(1)@Autowired与@Resource都可以用来装配bean,都可以写在字段或s**ette**r方法上
(2)@Au**towired默**认按类型装配,默认情况下必须要求依赖对象存在,如果要允**许null值,可以设置它的required属性为f**alse。如果想使用名称装配可以结合**@Qualifier注**解进行使用。
(3)@Resource,默认按照名称进行装配,名称可以通过nam**e**属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配**的bea**n时才**按**照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
30**、**RPC 的**实现基础?**
⚫ 需要有非常高效的网络通信,比如一般选择Netty作为网络通信框架;
⚫ 需要有比较高效的序列化框架,比如谷歌的Protobuf序列化框架;
⚫ 可靠的寻址方式(主要是提供服务的发现),比如可以使用Zookeeper来注册服务等 等;
⚫ 如果是带会话(状态)的RPC调用,还需要有会话和状态保持的功能;
31**、CMS,G1 垃圾回收器中的三色标记了解**吗?
三色标记算法思想
三色标记法是一种垃圾回收法,它可以让JVM不发生或仅短时间发生STW(Stop The World),从而达到清除JVM内存垃圾的目的。
三色标记法将对象的颜色分为了黑、灰、白,三种颜色。
黑色:该对象已经被标记过了,且该对象下的属性也全部都被标记过了。(程序所需要的对象);
灰色:对象已经被垃圾收集器扫描过了,但是对象中还存在没有扫描的引用(GC需要从此对象中去寻找垃圾);
白色:表示对象没有被垃圾收集器访问过,即表示不可达。
CMS 解决办法:增量更新
在应对漏标问题时,CMS使用了增量更新(Increment Update)方法来做,在一个未被标记的对象(白色对象)被重新引用后,引用它的对象若为黑色则要变成灰色,在下次二次标记时让GC线程继续标记它的属性对象(但还是存在漏标的问题)。
CMS 另两个致命缺陷
CMS采用了Mark-Sweep算法,最后会产生许多内存碎片,当到一定数量时,CMS无法清理这些碎片了,CMS会让Serial Old垃圾处理器来清理这些垃圾碎片,而Serial Old垃圾处理器是单线程操作进行清理垃圾的,效率很低。
所以使用CMS就会出现一种情况,硬件升级了,却越来越卡顿,其原因就是因为进行Serial Old GC时,效率过低。
解决方案:使用Mark-Sweep-Compact算法,减少垃圾碎片
调优参数(配套使用):
-XX:+UseCMSCompactAtFullCollection 开启CMS的压缩
-XX:CMSFullGCsBeforeCompaction 默认为0,指经过多少次CMS FullGC才进行压缩 当JVM认为内存不够,再使用CMS进行并发清理内存可能会发生OOM的问题,而不得不进行Serial Old GC,Serial Old是单线程垃圾回收,效率低
解决方案:降低触发CMS GC的阈值,让浮动垃圾不那么容易占满老年代
调优参数:
-XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,让老年代占用率达到该值就进行CMS GC
G1**解决办法:SATB**
SATB(Snapshot At The Beginning), 在应对漏标问题时,G1使用了SATB方法来做,具体流程:
⚫ 在开始标记的时候生成一个快照图标记存活对象
⚫ 在一个引用断开后,要将此引用推到GC的堆栈里,保证白色对象(垃圾)还能被GC线 程扫描到(在**write barrier(写屏障)**里把所有旧的引用所指向的对象都变成非白的)
⚫ 配合Rset,去扫描哪些Region引用到当前的白色对象,若没有引用到当前对象,则回收 G1**会不会进行FullGC?**
会,当内存满了的时候就会进行Full GC;且JDK10之前的Full GC,为单线程的,所以使用G1需要避免Full GC的产生。
解决方案:
⚫ 加大内存;
⚫ 提高CPU性能,加快GC回收速度,而对象增加速度赶不上回收速度,则Full GC可以避 免;
⚫ 降低进行Mixed GC触发的阈值,让Mixed GC提早发生(默认45%)
还没有评论,来说两句吧...