测试部分
1.测试计划书中要包含的内容
(1)目的与范围 (2)规程 (3)测试方案与方法 (4)测试的准入准出
(5)测试的计划(流程、时间安排、人员安排)(6)环境配置
(7)交附件
2.测试用例要包含的内容
(1)测试项目 (2)编号 (3)测试标题 (4)优先级 (5)预置条件 (6)操作步骤
(7)测试数据 (8)预期结果
3.一个好的bug要包含的内容
(1)编号 (2)严重程度与优先级 (3)简要描述 (4)出现的模块
(5)详细的现象描述,截图、录像等 (6)出现bug的测试环境,产生条件等等
4.如何保证软件质量
(1)需求要吃透
(2)严格按照测试流程执行,多考虑用户测试场景,使用测试用例设计方法,多评审
(3)要有良好的测试执行,测试用例通过率达到90%以上,多轮测试,进行探索性测试,使用工具来管理我们的测试过程
5.对测试开发的理解
测试开发首先离不开测试,而软件测试是指,在规定的条件下对程序进行操作,以发现程序错误
,衡量程序质量,并对其能否满足设计要求进行评估的过程。
而且,现在不仅仅是通过手工测试来发现bug,也需要编写脚本,测试工具来完成自动化测试。因此,对于测试开发人员来说,他除了保证产品质量外,还需要编写脚本以及开发测试工具。
linux部分
1.查看日志文件中的关键字(字节一面问到了)
(1)使用cat -n filename | grep "关键字"
cat filename | grep -C 10 '关键字' (显示日志里匹配字串那行以及前后10行)cat filename | grep -B 10 '关键字' (显示匹配字串及前10行)cat filename | grep -A 10 '关键字' (显示匹配字串及后10行)
(2)使用tail -f 日志文件名 | grep 关键字
tail -f xxx.log ----实时刷新最新日志
tail -10f xxx.log --------实时刷新最新的10行日志
tail -10f xxx.log | grep [关键字] -------查找最新的10行中与关键字匹配的行
tail -10f xxx.log | grep '2021-02-04 11:4[0-9]' ------查找最新的10行中时间范围在2021-02-04 11:40-2021-02-04 11:49范围中的行
tail -10f xxx.log | grep -A 5 [关键字] ----------查看最新的10行中与关键字匹配的行加上匹配行后的5行
(3)使用vi命令
mysql部分
1.redolog与undolog的区别
(1)redo log:记录的是数据页的物理变化,服务宕机可用来同步数据
(2)undo log记录的是逻辑日志,当事务回滚时,通过逆操作恢复原来的数据
(3)redo log保证了事务的一致性,undo log保证了事务的原子性和一致性
java多线程部分
1.如何保证线程按顺序执行
使用线程中的join()来执行
2.notify()与notifyAll()的区别
notify():随机唤醒一个等待线程
notifyAll():唤醒所有等待线程
3.Java中wait与sleep方法的不同
共同点:
(1)wait,wait(Long)与sleep(long)都是让线程暂时放弃cpu的使用权,进入阻塞状态
不同点:
(1)sleep是Tread中的方法,wait是object中的方法
(2)wait需要synchronized配合使用
(3)wait方法会释放对象锁,允许其他线程获得对象锁,sleep在synchronized中运行不会释放对象锁
4.如何停止一个正在运行的线程
(1)使用退出标志
(2)使用stop方法强行终止,已作废
(3)使用interrupt方法中断线程(打断已经阻塞的线程会报错)
5.synchronized的底层原理
(1)synchronized对象锁采用互斥的方式使得同一时刻只有一个线程能拥有对象锁
(2)底层由monitor实现,monitor是jvm级别的对象(C++),线程获得锁需要使用对象锁关联monitor
(3)在monitor内部有三个属性owner、entrylist、waitset
进阶:
Java的Synchronized有偏向锁,轻量级锁,重量级锁三种形式,分别对应锁只能被一个线程持有、不同线程交替持有,多线程竞争三种情况。
重量级锁:底层使用Monitor实现,里边涉及用户态到内核态的切换,进程的上下文切换,成本较高,性能比较低。
轻量级锁:线程加锁的时间是错开的(也就是没有多次竞争),可以使用轻量级锁来优化,相对重量级锁性能提高很多。每次修改都是CAS操作,保证原子性
偏向锁:一段很长时间内都只能被一个线程使用锁,可以使用偏向锁,在第一次获得锁时,会有一个CAS操作,之后该线程再获取锁,只需要判断mark word是否是自己的线程Id即可,而不是开销比较大的CAS操作
6.JMM(Java内存模型)
JMM(Java内存模型),定义了共享内存中多线程程序读写操作行为规范,通过这些规则来规范对内存的读写操作来保证指令的正确性
JMM把内存区域分为两块,一块是私有线程的工作区域(工作内存),一块是所有线程的共享区域(主内存)
线程与线程之间相互隔离,线程跟线程交互需要通过主内存
7.对CAS的理解
CAS是基于乐观锁的思想,最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃点亏重试呗
底层调用Unsafe类中的方法实现,都是操作系统提供,其他语言实现
Synchronized是基于悲观锁的思想;最悲观的估计,得当着其他线程来修改共享变量,我上锁你们都想改,我改完了开锁,你们才有机会。
8.谈谈你对volatile的理解
(1)保证线程间的可见性
对volatile修饰共享变量,能够防止编译器等优化发生,让一个线程对共享变量的修改对另一个线程可见
(2)禁止进行指令重排序
指令重排:用volatile修饰共享会在读写共享变量时加入不同的屏障,阻止其他读写操作越过屏障,从而达到重排序的效果
9.Synchronized和lock有什么区别
(1)语法层面:
synchronized是关键字,源码在jvm中,用C++实现
lock是接口,源码由jdk提供,用java语言实现
使用synchronized时,退出同步代码块,锁会自动释放,而lock必须使用unlock方法手动释放
(2)功能层面
二者均属于悲观锁,都具备基本的互斥、同步、重入功能
lock提供了许多synchronized不具备的功能,例如公平锁、可打断、可超时、多条件变量
lock有适合不同场景的实现,如:ReentrantLock,ReentrantReadWriteLock(读写锁)
(3)性能层面
在没有竞争时,synchronized做了很多优化,如偏向锁,轻量级锁,性能不赖
在竞争激烈时,Lock的实现通常会提供更好的性能
9.死锁产生的条件
互斥条件,请求与保持条件,循环等待条件,不可剥夺条件
spooling技术,静态分配(一次性分配需要的所有资源),有序的请求资源,要么全拿要么直接抢别人占有的资源
计算机网络部分
1、Get与Post请求区别
(1)安全性:GET数据存储在url中,POST存储在请求体中,post安全度高
(2)对数据长度的限制:GET对数据长度有限制,POST没有
(3)编码方式:GET进能对url编码,POST支持多种编码方式
(4)数据类型限制:GET仅支持ASCII字符,POST对数据类型不作限制(对二进制数据也支持)
JVM部分
JVM组成部分
1、JVM是什么?
Java程序的运行环境
好处:
(1)一次编写,到处运行
(2)自动内存管理,垃圾回收机制
2、JVM由哪些部分组成,运行流程
(1)Java转换为字节码
(2)把字节码加载到内存
(3)字节码翻译为底层系统指令
(4)C/C++实现
3、你能详细介绍一下堆吗?
(1)堆是线程共享的区域。主要用来存放对象实例,数组等,内存不够则抛出OutOfMemoryError异常。
(2)组成:年轻代和老年代
老年代被划分为三部分,Eden区和两个大小严格相等的Survivor区
老年代主要保存生命周期长的对象,一般是一些老的对象
(3)Jdk1.7和1.8区别
1.7中有一个永久代,存储的是类信息,静态变量,常量,编译后的代码
1.8移除了永久代,把数据 存储在本地内存的元空间中,防止内存溢出
4、什么是虚拟机栈
(1)每个线程运行时需要的内存,称为虚拟机栈,先进后出
(2)每个栈有多个栈帧组成,对应着每次方法调用时所占用的内存
(3)每个线程只能有一个活动栈帧,对应着当前执行的那个方法
5.垃圾回收是否设计栈内存
不涉及,垃圾回收主要是指堆内存,当栈帧弹栈以后,内存就会释放
6.栈内存分配越大越好吗?
未必,默认栈大小通常为1024kB
栈帧过大会导致线程数变少,例如,机器总内存512m,目前能活动的线程数则为512个,如果把栈内存改为2048k,那么能活动的栈帧就会减半,可以并发的线程数也减少
栈内存越大线程数就会越少,因为物理内存大小是固定的,栈内存越大,可运行线程就越少。比如,一个线程使用栈内存,假设使用1M的内存,物理内存500M,理论上就可以有500个线程同时运行。如果每个线程设置2M,那么只能同时有250个线程运行。所以栈内存分配越大并不是越好,它分配大了通常只是能够进行多次的方法递归调用,而不会增快程序的运行效率,反而会影响线程数目的变少。一般采用默认的就可以,不必在程序启动的时候手动修改。
7.方法中局部变量是否线程安全
(1)如果局部变量没有逃离方法的作用范围,他是线性安全的
(2)如果是局部变量引用了对象,并没有逃离方法的作用范围,需要考虑线程安全
8.栈内存溢出情况
(1)栈帧过多导致内存溢出,典型问题:递归调用
(2)栈帧过大导致栈内存溢出
9.堆栈的区别
(1)栈内存一般用来存储局部变量和方法调用,但是堆内存是用来存储java对象和数组的堆。堆会进行CG垃圾回收,而栈不会
(2)栈内存是线程私有的,堆内存是线程共享的
10.能不能解释一下方法区
(1)方法区是各个线程共享的内存区域
(2)主要存储类的信息,运行时常量池,类加载器等等
(3)虚拟机启动时创建,关闭虚拟机时释放
(4)如果方法区域中的内存无法满足内存空间,会抛出异常
11.介绍一下运行时常量池
常量池:可以看作一张表,虚拟机指令根据这张表找到要执行的类名,方法名,参数类型,字面量等信息
当类被加载时,它的常量池信息就会放入运行时常量池,并把里面的符号地址转换为真实地址
12.你听说过直接内存吗?
(1)直接内存并不属于JVM内存结构,不由JVM进行管理。是虚拟机的系统内存。
(2)在常见的NIO操作时,用于数据缓冲区,分配回收成本较高,但读写性能高,不受JVM内存回收管理
(3)常见的IO数据拷贝,不仅涉及到用户态和内核态的切换,在磁盘-系统缓冲区-java缓冲区之间还存在一层拷贝,这会影响IO操作的性能
(4)直接内存就是系统和java代码都可以访问到的一块缓冲区
JVM类加载器部分
1.什么是类加载器,类加载器有哪些?
(1)JVM只会运行二进制文件,类加载器将Java字节码文件加载到JVM中,从而让java文件可以运行起来
(2)启动类加载器,拓展类加载器,应用类加载器,自定义类加载器
2.什么是双亲委派模型,为什么采用双亲委派机制
双亲委派机制是加载某一个类时,先委托上一级的加载器进行加载,如果上级加载器也有上级,则会继续向上委托,如果上级加载器没有被加载,则交给子加载器进行加载
采用双亲委派机制的原因
1)通过双亲委派机制可以避免某一个类被重复加载,保证唯一性
2)为了安全,保证类库API不会被加载
3.说一下类装载的执行过程
加载:查找和导入class文件
验证:保证加载类的准确性
准备:为类变量分配内存并设置类变量初始值
解析:把类中的符号引用转化为直接引用
初始化:对类的静态变量、静态代码块执行那个初始化操作
使用:JVM开始从入口方法开始执行用户的程序代码
卸载:当用户代码执行完毕后,JVM开始销毁创建的Class对象
CG垃圾回收部分
1.垃圾什么时候可以被回收?定位垃圾的两种办法
(1)没有引用指向
(2)引用计数法(循环引用会导致其失效 ),可达性分析法
2.垃圾回收算法有哪些?
(1)标记清除算法
先试用可达性分析算法进行标记,再对这些标记可回收内容进行回收
优点:标记和清除速度更快
缺点:碎片化较为严重,内容不连贯
(2)标记整理算法(常用于老年代)
(3)复制算法(年轻代)
优点:在垃圾对象比较多的情况下,效率较高
清理后,内存无碎片
缺点:分配两块内存空间,同一时刻,只能使用一半,内存使用率较低