晚上正在公司加班,突然收到阿里的电话,其实之前已经面试过阿里的杭州岗位,只不过连电话面试都没通过就被刷下来了。现在又来了一次,被问到了以下的题目:
1、Java引用类型有哪些
Java不同于C/C++,不需要程序员来管理内存,Java会自己进行内存回收,回收操作通过垃圾收集器线程进行处理,jvm在进行垃圾收集的时候,需要判断哪些对象需要销毁,这时就与这几种引用类型相关。
强引用(Strong Reference)
强引用类型是我们平时写代码的时候最常用的引用,是Java默认的引用类型。有强引用存在的对象永远都不会被gc收集,所有内存不够用是,JVM宁愿抛出OutOfMemorError这样的错误,也不愿意将强引用对象进行回收。
例如: Sample sample = new Sample(); //sample这个引用就是强引用
软引用(Soft Reference)
软引用在java.lang.ref包中有与之对应的类java.lang.ref.SoftReference。被软引用执行的对象不会被垃圾收集器收集,除非jvm使用内存不够了,才会对这类对象进行销毁,释放内存。
例如: SoftReference
弱引用(Weak Reference)
弱引用会被jvm忽略,也就是说在GC进行垃圾收集的时候,如果一个对象只有弱引用指向它,那么和没有引用指向它是一样的效果,jvm都会对它进行果断的销毁,释放内存。
例如: WeakReference
虚幻引用(Phantom References)
虚幻引用和弱引用的回收机制差不多,都是可以被随时回收的。但是不同的地方是,他的构造方法必须强制传入ReferenceQueue,引用在jvm回收前(软引用和弱引用都是回收后),会将PhantomReference对象加入ReferenceQueue;另外,PhantomReference.get()方法永远返回空,不过对象有没有被回收。
PhantomReference
关于四种引用类型的详细解释,请参考博客( https://blog.csdn.net/rodbate/article/details/72857447)
2、AOP的原理和实现方式
AOP(Aspect Oriented Programing)意为面向切面编程,可以看做是对OOP的补充,可以再不修改原有代码逻辑的情况下,实现功能添加和增强。基于代理思想,对原理目标对象创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强。
应用场景:
日志记录、监控性能、权限控制、缓存优化、事务管理、统一异常处理等等
Spring AOP有两种实现方式:
(1)JDK动态代理。通过实现InvovationHandler几口的回调函数,定义回调中的invoke方法,在invoke中可以添加增强功能,实现AOP。当调用实例的方法时,会生成代理对象。
(2)Cglib动态代理。在实际开发中,可能需要对没有实现接口的类增强,用JDK动态代理的方式就没法实现。采用Cglib动态代理可以对没有实现接口的类产生代理,实际上是生成了目标类的子类来进行增强。
详细解释请参考( https://blog.csdn.net/u010452388/article/details/80868392)
3、ArrayList是不是线程安全的,扩容机制是什么
所谓线程安全是指在并发的情况下,代码经过多线程使用,线程的调度顺序不影响任何结果。ArrayList是线程不安全的,我们查看ArrayList的源码中add方法的实现,发现有这么一句代码:
elementData[size++] = e;
这行代码实际上是符合语句,CPU是分两步来执行的:
1. elementData[size] = e;
2. size ++;
假设A线程执行完第一条语句时,CPU暂停执行A线程转而去执行B线程,此时ArrayList的size并没有加一,这时在ArrayList中B线程就会覆盖掉A线程赋的值,而此时,A线程和B线程先后执行size++,便会出现值为null的情况;至于结果三中出现的ArrayIndexOutOfBoundsException异常,
则是A线程在执行ensureCapacity(size+1)后没有继续执行,此时恰好minCapacity等于oldCapacity,B线程再去执行,同样由于minCapacity等于oldCapacity,ArrayList并没有增加长度,B线程可以继续执行赋值(elementData[size] = e)并size ++也执行了,此时,CPU又去执行A线程的赋值操作,由于size值加了1,size值大于了ArrayList的最大长度,
因此便出现了ArrayIndexOutOfBoundsException异常。
既然ArrayList是线程不安全的,但如果需要在多线程中使用,可以采用list
4、Spring bean的生命周期
Bean实例生命周期的执行过程如下:
Spring对bean进行实例化,默认bean是单例;
Spring对bean进行依赖注入;
如果bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法;
如果bean实现了BeanFactoryAware接口,spring将调用setBeanFactory方法,将BeanFactory实例传进来;
如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization方法将被调用;
如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization接口方法将被调用;
此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用
5、jdk8和jdk7内存模型最大的不同
在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代
在JDK1.7 字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代
在JDK1.8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace)
JDK1.8与1.7最大的区别是1.8将永久代取消,取而代之的是元空间,既然方法区是由永久代实现的,取消了永久代,那么方法区由谁来实现呢,在1.8中方法区是由元空间来实现,所以原来属于方法区的运行时常量池就属于元空间了。元空间属于本地内存,所以元空间的大小仅受本地内存限制,但是可以通过-XX:MaxMetaspaceSize进行增长上限的最大值设置,默认值为4G,元空间的初始空间大小可以通过-XX:MetaspaceSize进行设置,默认值为20.8M,还有一些其他参数可以进行设置,元空间大小会自动进行调整。
6、有没有处理过线上告警问题
回答:没有处理过
以上是问的问题和答案,还有一些问题不记得了,以上列出的问题没有完全回答上来,所以感觉gg了。