阿里一面感受【补面经】
链接:https://www.nowcoder.com/discuss/113050
阿里一面20min
说项目,说平常怎么学习java,说除了项目有自己开发过什么。
问基础
分代回收算法
对象分类
这种算法并不是一种新的算法,而是根据对象的存活周期的不同而将内存分为几块,分别是新生代、老年代和永久代。
新生代:朝生夕灭的对象(例如方法的局部变量等);
老年代:存活的比较久但还是要死的对象(例如缓存对象、单例对象等);
永久代:对象生成后几乎不灭的对象(例如加载过的类信息);
内存区域
新生代和老年代都在Java堆,永久代在方法区。
Java对象的回收
新生代:采用复制算法,新生对象一般存活率较低,因此可以不使用50%的内存空间作为空闲,一般的使用两块10%的内存。
作为空闲和活动区间,而另外80%的内存则是用来给新建对象分配内存的。一旦发生GC,将10%的活动区间与另外80%中存活的对象转移到10%的空闲空间,接下来,将之前90%的内存全部释放。以此类推,如图所示:
堆大小=新生代+老年代,新生代和老年代的比例为1:2,新生代细分为一块交大的Eden空间和两块较小的Survivor空间,分别被命名为from和to。
老年代:老年代中使用标记-清除或者标记-整理算法进行垃圾回收,回收次数相对较少,每次回收时间比较长。
方法区对象回收
永久代指的是虚拟机内存中的方法区,永久代垃圾回收 比较少,效率也比较低,但也必须进行垃圾回收,否则永久代内存不够用时仍然会抛出OutOfMemoryError异常。永久代也使用标记-清除或标记-整理算法进行垃圾回收。
对象如何进入老年代
对象优先在新生代的eden区分配内存,但是也并不绝对,下面几种情况对象会晋升到老年代。
1、大对象直接进入老年代。比如很长的字符串,或者很大的数组等。
2、长期存活的对象进入老年代。在堆中分配内存的对象,其内存布局的对象头中包含了GC分代年龄标记信息。如果对象在eden区出生,那么它的GC分代年龄初始值为1,每熬过一次Minor GC而不被回收这个值会增加1岁。当他的年龄到达一定的数值时(jdk1.7默认是15),就会晋升到老年代。
3、动态对象年龄判定。当Survivor空间中相同年龄所有对象的大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,而不需要达到默认的分代年龄。
新生代用的什么算法,老年代用的什么算法
新生代用的是复制算法,老年代用的是标记-清除或标记整理算法。
lock ,sychronized,volatile的区别
sychronized与volatile关键字区别:
1、volatle关键字解决的是变量在多个线程之间的可见性;而sychronized关键字解决的是多个线程之间访问共享资源的同步性。
2、volatile只能用于修饰变量,而synchronized可以修饰方法以及代码块。(volatile是线程同步的轻量级实现,所以volatile性能比synchronized要好,并且随着JDK新版本的发布,synchronized关键字在执行上得到很大的提升,在开发中使用synchronized关键字的比率还是比较大。)
3、多线程访问volatile不会发生阻塞,而synchronized会发生阻塞。
4、volatile能保证变量在多个线程之间的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和 公有内存中的数据做同步。
线程安全包含原子性和可见性两个方面。对于用volatile修饰的变量,JVM只是保证从主存加载到线程工作内存的值是最新的。一句话说明volatile的作用:实现变量在多个线程之间的可见性。
synchronized与lock的区别
1、lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现。
2、synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而lock在发生异常时,如果没有主动通过unlock()去释放锁,则很可能造成死锁现象,因此使用lock时需要在finally块中释放锁。
3、lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能响应中断。
4、通过lock可以知道有没有成功获取锁,而synchronized却无法办到。
5、lock可以提高多个线程进行读操作的效率(读写锁)。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
乐观锁,悲观锁
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁等,读锁写锁等,都是在操作之前先上锁。Java中synchronized和ReetrantLock等独占锁就是悲观锁思想的实现。
乐观锁:总是假设好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
乐观锁使用场景,使用乐观锁的产品或中间件
https://blog.csdn.net/strawqqhat/article/details/88743519
线程池参数
https://blog.csdn.net/strawqqhat/article/details/88748702
一个核心池为2,等待队列3,最大线程数10的线程池,已有两个线程运行任务,第三个任务进来时如何处理
判断等待队列中是否有地方存放该任务,如果有就将任务保存在等待队列中等待执行,没有就去判断最大可容纳的线程数,如果没有超出这个数量就去开创非核心线程执行任务,否则调用handler实现拒绝策略。
缓存线程池的参数如何设置,特性是什么
https://blog.csdn.net/strawqqhat/article/details/88749714
什么是数据库事务
数据库事务的性质,隔离级别
https://blog.csdn.net/strawqqhat/article/details/88564150
数据库如何实现串行化
可重复读实现了读取前后数据内容一致,
但是产生了数据条目不一致(幻读),
可串行化解决了这个问题,
串行化是最严格的事物隔离级别:
事物a 执行读写操作时,会锁定检索的数据行范围(范围锁),这种锁会阻止其他事物在本范围内的一切操作,
只有事物a执行完毕,提交事物后,才会释放范围锁,这样就避免了幻读。
但是,实际项目中,这个级别的隔离很少用,因为并发性能低,容易死锁。
缓存数据处理【说的LRU】
https://blog.csdn.net/bie_niu1992/article/details/77750220
场景题 关于redis的
分布式锁,如何添加,放在什么位置