2020年JAVA后端面试

2020年JAVA后端面试

springboot aop切面相关试题

如何新建一个AOP切面

以经典的演出和观众为例,如果一场演出没有观众的话,不能称之为演出。从演出的角度看,观众是十分重要的,但是对演出本身,或者说对于台上的表演者来说,观众并不是核心,这是一个单独的关注点。因此,将观众定义为一个切面,并将其应用到演出上是较为明智的做法。

创建切点
切点就是常见的接口,interface。比如该案例中我们定义一个Performance接口;

创建引用切点的实例
即创建台上表演者类Dancer,以便后期用于测试。

创建切面
建立Audience为切面类,即观众类。该类通过注解@Aspect定义为切面,@Around注释表明watchPerformance()方法会作为performance()切点的环绕通知。

编写配置文件
编写配置文件PerforConfig,注入观众bean和表演者bean

测试

JVM的运行参数

标准参数
非标准参数
非Stable参数

标准参数
verbose:class
verbose:gc
verbose:jni

非标准参数
Xms512m 初始内存
Xmx512 JVM最大内存
Xmn200m 年轻代代销
Xss128k 线程的堆栈大小

非Stable参数

用-XX作为前缀的参数列表在jvm中可能是不健壮的,SUN也不推荐使用,后续可能会在没有通知的情况下就直接取消了

-XX:-UseConcMarkSweepGC 对老生代采用并发标记交换算法进行GC
-XX:-UseParallelGC 启用并行GC
-XX:-UseSerialGC 启用串行GC

上面三个参数代表着jvm中GC执行的三种方式,即串行、并行、并发;
串行(SerialGC)是jvm的默认GC方式,一般适用于小型应用和单处理器,算法比较简单,GC效率也较高,但可能会给应用带来停顿;
并行(ParallelGC)是指GC运行时,对应用程序运行没有影响,GC和app两者的线程在并发执行,这样可以最大限度不影响app的运行;
并发(ConcMarkSweepGC)是指多个线程并发执行GC,一般适用于多处理器系统中,可以提高GC的效率,但算法复杂,系统消耗较大;

性能调优参数
-XX:MaxNewSize=size 新生成对象能占用内存的最大值
-XX:MaxPermSize=64m 老生代对象能占用内存的最大值
-XX:NewSize=2.125m 新生代对象生成时占用内存的默认值

调试参数
-XX:HeapDumpPath=./java_pid.hprof
指定导出堆信息时的路径或文件名
-XX:-HeapDumpOnOutOfMemoryError
当首次遭遇OOM时导出此时堆中相关信息
-XX:-PrintConcurrentLocks
遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同

JVM 内存模型

根据java虚拟机规范,java虚拟机管理的内存将分为下面五大区域。

Runtime Constant Pool 方法区
Heap 堆区
JVM Stacks 虚拟机战
Native Method Stack 本地方法栈
Program Counter Register 程序计数器

程序计数器 这块内存区域是虚拟机规范中唯一没有OutOfMemoryError的区域。
同计数器也为线程私有,生命周期与相同,就是我们平时说的栈,栈描述的是Java方法执行的内存模型。
每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
Java虚拟机栈可能出现两种类型的异常:
线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。
虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。
堆是java虚拟机管理内存最大的一块内存区域,因为堆存放的对象是线程共享的,所以多线程的时候也需要同步机制。
方法区同堆一样,是所有线程共享的内存区域,为了区分堆,又被称为非堆。
用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中。
jdk8真正开始废弃永久代,而使用元空间(Metaspace)
java虚拟机对方法区比较宽松,除了跟堆一样可以不存在连续的内存空间,定义空间和可扩展空间,还可以选择不实现垃圾收集

JVM堆内存信息查看的LINUX命令

pgrep -lf java
查看当前JAVA进程的PID

jmap -heap PID
查看java堆的详细信息

jmap -histo
查看java堆中对象的相关信息,包含数量以及空间大小

jstat -gcutil pid
查看监控heap size 和 jvm 垃圾回收情况

Redis为什么能用于高并发

I/O 多路复用

Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务,而 I/O 多路复用就是为了解决这个问题而出现的。

Reactor 设计模式
Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)

基于内存

多线程死锁处理以及避免

在Java中使用多线程,就会有可能导致死锁问题。死锁会让程序一直卡住,不再程序往下执行。我们只能通过中止并重启的方式来让程序重新执行。

造成死锁的原因

当前线程拥有其他线程需要的资源
当前线程等待其他线程已拥有的资源
都不放弃自己拥有的资源

锁顺序死锁
我们的线程是交错执行的,那么就很有可能出现以下的情况:
线程A调用leftRight()方法,得到left锁
同时线程B调用rightLeft()方法,得到right锁
线程A和线程B都继续执行,此时线程A需要right锁才能继续往下执行。此时线程B需要left锁才能继续往下执行。
但是:线程A的left锁并没有释放,线程B的right锁也没有释放。
所以他们都只能等待,而这种等待是无期限的–>永久等待–>死锁

动态锁顺序死锁
如果两个线程同时调用transferMoney()
线程A从X账户向Y账户转账
线程B从账户Y向账户X转账
那么就会发生死锁。

协作对象之间发生死锁
并且在操作途中是没有释放锁的
这就是隐式获取两个锁(对象之间协作)…
这种方式也很容易就造成死锁…

避免死锁的方法

固定加锁的顺序(针对锁顺序死锁)
开放调用(针对对象之间协作造成的死锁)
使用定时锁

死锁检测

JconsoleJDK自带的图形化界面工具,使用JDK给我们的的工具JConsole
Jstack是JDK自带的命令行工具,主要用于线程Dump分析。

发生死锁的原因主要由于:
线程之间交错执行
解决:以固定的顺序加锁
执行某方法时就需要持有锁,且不释放
解决:缩减同步代码块范围,最好仅操作共享变量时才加锁
永久等待
解决:使用tryLock()定时锁,超过时限则返回错误信息
在操作系统层面上看待死锁问题

你可能感兴趣的:(Java,面试)