java后端研发经典面试题总结

本文详细总结了Java后端开发的面试重点,涵盖AtomicInteger原理、synchronized与Lock的区别、垃圾回收机制、JVM调优、Spring框架、锁的实现、线程与进程的区别、线程池管理、HTTP协议、数据库连接池等内容,深入探讨了各知识点的原理与应用场景,是Java后端面试的重要参考资料。
摘要由CSDN通过智能技术生成

1.标记-清除算法

标记-清除算法是最基本的算法,和他的名字一样,分为两个步骤,一个步骤是标记需要回收的对象。在标记完成后统一回收被标记的对象。这个算法两个问题。一个是效率问题,标记和清除的效率不高。第二个问题是标记-清除之后会有大量不连续的碎片空间,如果我们需要更大的连续内存就必须GC。

2.复制算法

复制算法,不同于标记-清除,复制算法大多数用于新生代,它需要大小相等的两块内存,每次只使用一块内存,当GC的时候会把这块内存存活的对象复制到另外一块内存上面,解决了时间效率和空间碎片问题。在新生代中会把他分为三个内存一个Eden 两个Survivor默认是8比1,开始会使用一个Eden和一个Surivivor装载内存,清除时会将这两个保留的对象都保存另外在Survivor中,并且年龄加1(以后提升为老年代),如果超出了Survivor中的限制会用老年代的内存担保。

3.标记-整理算法

主要特点是,解决了碎片问题。标记整理算法,标记过程和第一算法一样,但是他处理的时候会让存活的对象向一边移动解决了空间碎片问题,用于老年代的处理。

4.分代收集算法

分代收集算法不是新思想,只是把上面的算法结合起来了。

AtomicInteger实现原理

AtomicInteger使用value来保存值,value是volatile的,保证了可见性。

对于get方法直接返回value,对于自增一或者添加值使用了CAS自旋锁,使用了一个死循环,如果cas返回为true就可以退出循环。对于CAS 全称是compare and swap比较和交换,CAS需要三个操作数,一个是变量内存地址,一个是excepted过期值,一个是现在要更新的值,我们操作的时候仅当V符合旧预期的值的时候才能更新我们新的。对于它的自增的操作,首先是卸载一个for循环里面然后获得当前的值,给这个值+1,然后进行cvs要是失败会再次Get()最新值再次写.

synchronized和lock的区别

主要有三个区别1、用法区别,性能区别,锁机制的区别。

(1)对于用法区别:synchronized可以在方法中上使用也可以在特定代码块中使用,括号中表示需要锁的对象,如果在方法上就是对该对象的锁,如果是在类的方法上就是类的锁,使用Lock必须自己用代码显示申明何时开启锁,何时关闭锁。synchronized是jvm的底层实现,而Lock是由代码执行。

(2)对于性能的区别:ReentrantLock 功能上要多于synchronized,多了锁投票,定时锁等。如果在小规模的竞争上synchronized效率比较高,如果在大规模的竞争上synchronize就比较低而Lock基本不变、

(3)锁的机制也不同:synchronized获得锁和释放锁都是在块中,都是自动释放,不会引起死锁,而Lock需要自己定位释放,不然会引起死锁。在Lock中也使用了tryLock方法用非阻塞的方式获取锁。

在lock中用一个锁变量和队列维护同步。

gc停顿原因,如何降低GC停顿

原因:gc停顿的意思就像是在整个分析期间冻结在某个时间点上,具体的原因是防止在分析的时候,对象引用关系还在不断的变化,如果没有GC停顿很有可能分析不准确。

如何降低:在Serial的老年代垃圾收集器中,会把所有线程的暂停,停下来收集哪些是死亡对象。在CMS和G1中都采取了初始标记、并发标记、短暂GC停顿重新标记,初始标记会直接记录能GC ROOTS 关联的对象,在并发标记的时候有一个线程来标记,这个时候对象的发生的变化都会记录下来,在重新标记的时候会修正,这样就会降低GC停顿时间

jvm如何调优,参数怎么调?如何利用工具分析jvm状态?

合理的分配内存,分配栈和堆的内存,在堆中我们还可以详细划分新生代和老年代的内存比例,在新生代中我们也可以划分Eden和Surivior的内存比例(调该比例大小),合理的划分内存区域大小,可以帮助我们jvm调优,我们采取合适的垃圾回收器,比如在新生代启用serial垃圾回收器,在老年代采用cms并发标记,可以降低GC停顿,当然也可以尝试去采用G1垃圾回收器

jvm中类加载过程

类加载到类被卸载过程包括7个阶段

1.加载 通过类的全限定名把类文件的二进制流加入进来,通过这个字节流(这个二进制流也是我们代理类的方法),然后通过这个二进制流把静态存储结构转化为运行时方法区的结构(不包括类变量,类变量在准备阶段),在内 存中生成一个Class对象,作为方法区访问的入口。

2.验证 验证是验证Class文件的字节流包含的信息是否符合当前虚拟机的要求规范,防止恶意攻击、

3.准备 在方法区为类变量分配内存和设置初始值,这个时候的初始值是数据的0值,不是我们定义的值,如果是常量的话准备阶段就会设置为我们定义的值

4.解析 将符号引用(这里的符号引用指的是字面量的形式,只需要无歧义地定位到目标)替换为直接变量

5.初始化 类初始化 阶段是我们加载过程的最后一步,执行类构造器,合并static语句,有static的顺序决定。

6.使用

7.卸载

Spring中bean的加载机制,bean生成具体步骤

**Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。**在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

spring中Bean的加载机制其实就是IOC容器的初始化,比如我们这里定义了一个IOC容器,BeanFactroy的子类ClassXMLPathApplicationContext,在他的构造函数中我们会把xml路径写进去这个步骤就是定位,接下来就是BeanDefiniton的载入,在构造函数当中有一个refresh()的函数,这个就是载入BeanDefinition的接口,这个方法进去之后是一个同步代码代码块,把之前的容器销毁和关闭创建了一个BeanFatroy,就像对我们的容器重新启动一样,然后我们对BeanDefiniton载入和解析解析完毕之后会把beanDefinition和beanName放入BeanFactory的HashMap中维护。在这里Bean已经被创建完成,然后我们就像IOC容器索要Bean,如果是第一次索要会触发依赖注入,会递归的调用gebBean实现依赖出入****.

讲下java锁的原理

对于synchronized关键字,在jvm中在编译的时候在同步块的前后形成监视进入和监视退出两个字节码,这两个字节码都需要一个引用类型的参数来指定要锁定和解锁的对象,如果指定了的话就使用指定的对象,如果没有指定看是类方法还是对象方法来决定,在执行监视进入的指令的时候,会判断能否进入,进入成功之后会把锁计数器加1,如果不成功就会继续等待和其他的线程竞争,出锁的时候会把锁计数器减1变为0,也就是释放了锁。在这里要说明一点java的线程时映射到系统原生线程之上,如果要阻塞或者唤醒一个线程都需要操作系统帮忙,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多的处理器时间。有可能比用户的代码执行时间还长。在jdk1.6之后对synchronized优化是非常的号的,比如锁粗化,锁自旋,锁消除。轻量级锁和偏向锁。

而对于ReentrantLock是代码上的实现

线程和进程的区别

进程是一段正在执行的程序,线程也叫作”轻量级进程“,他是程序执行的最小单元,一个进程可以有多个线程,各个线程之间共享程序的内存空间(比如说堆空间)及一些进程级的资源,进程和进程之间不能共享内存只能共享磁盘文件,线程也有4中状态:就绪,运行,挂起,死亡。

(新版本)进程是程序执行时的一个实例,从内核的观点看,进程的目的就是担当分配系统资源的基本单位。

线程是进程的一个执行流,是cpu调度和分配的基本单位,它是比进程更小的能独立运行的基本单位。一个进程由几个线程组成,线程和同属一个进程的其他的线程共享进程所拥有的全部资源。

进程-资源分配的最小单位。线程-程序执行的最小单位。

进程由独立的空间地址,线程没有单独的地址空间 同一进程内的共享进程的地址空间,只有自己独立的堆栈和局部变量。对于我们来说实现一个多线程的任务比实现一个多进程的任务好,

为什么分配线程比分配一般的对象更需要花费更大的代价?

首先他的资源非常“节俭”。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段,堆栈段和数据段,这是一种“昂贵”的多任务工作方式。而运行一个进程中的多个进程,他们彼此之间使用相同的地址空间,共享进程内的大部分数据,启动一个进程的时间远大于一个线程的时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值