面试题

原创
2019/06/04 14:20
阅读数 61

 1.分析一下JDK优化的逃逸分析和消除,反射的实现原理?

在编程语言的编译优化原理中,分析指针动态范围的方法称之为逃逸分析。
通俗一点讲,就是当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了逃逸。

而用来分析这种逃逸现象的方法,就称之为逃逸分析。

什么是逃逸分析

  逃逸分析,是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。 逃逸分析(Escape Analysis)算是目前Java虚拟机中比较前沿的优化技术了。

 

逃逸分析的原理

  Java本身的限制(对象只能分配到堆中),我可以这么理解了,为了减少临时对象在堆内分配的数量,我会在一个方法体内定义一个局部变量,并且该变量在方法执行过程中未发生逃逸,按照JVM调优机制,首先会在堆内存创建类的实例,然后将此对象的引用压入调用栈,继续执行,这是JVM优化前的方式。然后,我采用逃逸分析对JVM进行优化。即针对栈的重新分配方式,首先找出未逃逸的变量,将该变量直接存到栈里,无需进入堆,分配完成后,继续调用栈内执行,最后线程执行结束,栈空间被回收,局部变量也被回收了。如此操作,是优化前在堆中,优化后在栈中,从而减少了堆中对象的分配和销毁,从而优化性能。

 

逃逸的方式

  方法逃逸:在一个方法体内,定义一个局部变量,而它可能被外部方法引用,比如作为调用参数传递给方法,或作为对象直接返回。或者,可以理解成对象跳出了方法。

  线程逃逸:这个对象被其他线程访问到,比如赋值给了实例变量,并被其他线程访问到了。对象逃出了当前线程。

 

逃逸分析的好处

  如果一个对象不会在方法体内,或线程内发生逃逸(或者说是通过逃逸分析后,使其未能发生逃逸)

  1. 栈上分配

    一般情况下,不会逃逸的对象所占空间比较大,如果能使用栈上的空间,那么大量的对象将随方法的结束而销毁,减轻了GC压力

  2. 同步消除

    如果你定义的类的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。

  3. 标量替换

    Java虚拟机中的原始数据类型(int,long等数值类型以及reference类型等)都不能再进一步分解,它们可以称为标量。相对的,如果一个数据可以继续分解,那它称为聚合量,Java中最典型的聚合量是对象。如果逃逸分析证明一个对象不会被外部访问,并且这个对象是可分解的,那程序真正执行的时候将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替。拆散后的变量便可以被单独分析与优化,可以各自分别在栈帧或寄存器上分配空间,原本的对象就无需整体分配空间了。

1、在运行状态中,对于任意一个类,都能够知道这个类的属性和方法。

2、对于任意一个对象,都能够调用它的任何方法和属性。

这种动态获取信息以及动态调用对象的方法的功能称为JAVA的反射

2.讲一下你理解的双亲委派制度

双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器。每个类加载器都是如此,只有在父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载。

3.多线程是如何控制并发访问class?

4.hashmap里的红黑数原理?

5.redis的哨兵机制和使用场景,缓存雪崩的应对方式,redis布隆过滤器,缓存穿透怎么预防

  • 布隆过滤器说某个元素在,可能会被误判。
  • 布隆过滤器说某个元素不在,那么一定不在。
  • 缓存穿透:

    认识

    缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

    解决办法:

    对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。还有最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

    也可以采用一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

    缓存雪崩

    认识

    如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。

    这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。

    解决方法

    在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

    可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存

    不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀

    做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部