- java 8中基本类型
int byte short long char double float boolean
4 1 2 8 2 8 4
2.mysql 和oracle的数据库事务
数据库事务的四大特性(acid): 原子性,一致性,隔离性,持久性
不考虑数据库事务的隔离性,会出现以下几种并发问题。:
脏读: 一个事务读取了另外一个事务未提交的数据。
幻读:是针对的一批数据,比如update了一批数据,但是此时刚好插入了一小数据。
不可重复读:多次事务读取的结果不一致。是因为在读取的过程中,其他事务修改了数据,针对一条数据。
数据库的隔离级别:
mysql的隔离级别 (默认是 可重复读)
read uncommitted 读取未提交的事务,读取速度快,但是什么都不能解决
read commit 读取提交的事务结果, 可以防止脏读
reapeat read 可重复读。 可避免脏读,不可重复读 。
serializable 串行化,可避免所有的,但是效率低。
oracle的隔离级别:
read commit: 可避免脏读 默认隔离级别
seriailizable 串行化,可避免所有的
oracle中有undo,所以它天生读写不阻塞,所以不会发生脏读
undo也可称之为数据库的rollback数据,它用户确保数据的一致性,当执行数据库的DML时,事务操作前的数据称为undo记录,undo段用来保存事务所修改数据的的旧值,其中存储着被修改数据块的位置以及修改前数据
oracle undo
3 hashmap的原理以及currenthashmap的原理
主要说 currenthashmap 吧:
1.7 的时候使用 segment(extend retenLock) 锁分段技术 一个hashmap中有16个segment数组,每个数组中又有hashEntry,是一种数组加链表的格式。
1.8的时候是使用cas以及synchronize的关键字来保证数据在并发时的安全性。 当数组的长度大于8 的时候转换为红黑树。 红黑树的特点简单来说是 根节点为黑的。
- cas aqs
队列同步器AbstractQueuedSynchronizer(以下简称同步器),是用来构建锁或者其他同步组件的基础框架。
同步器包含两个节点类型的应用,一个指向头节点,一个指向尾节点,未获取到锁的线程会创建节点线程安全(compareAndSetTail)的加入队列尾部。同步队列遵循FIFO,首节点是获取同步状态成功的节点
CAS,在Java并发应用中通常指CompareAndSwap或CompareAndSet,即比较并交换
CAS是一个原子操作,它比较一个内存位置的值并且只有相等时修改这个内存位置的值为新的值,保证了新的值总是基于最新的信息计算的,如果有其他线程在这期间修改了这个值则CAS失败。CAS返回是否成功或者内存位置原来的值用于判断是否CAS成功。
cas aqs 以及synchronize的实现原理
5 jvm
jvm的构造:
6 数组遍历
java.util包下的集合都是快速失败(fail-fast) 。java.util.currentutil下则是安全失败(fail-safe)
比如ArrayList的iterator的迭代中。
fail-fast
这里只是判断modCount!=expectedModCount。如果刚好相等。则不会异常。所以##不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
java.util.currentutil下的安全失败,则是在遍历的时候复制源集合。所以在遍历过程中。源集合发生变化,不会影响到当前遍历。
-
数据库索引
单列索引
唯一索引
组合索引(遵循“最左前缀”原则,把最常用作为检索或排序的列放在最左,依次递减)
。 索引的实现原理,mysql
BTree索引和B+Tree索引
8 myisam 和 innerdb的区别
从知乎上找找看
9 连接池
连接池的参数含义
连接池的拒绝策略
10,jvm的内存划分
- synchronize 的含义, 在synchronize的修饰在静态类上的时候,代表锁是类对象,而修饰非静态方法的时候是对象的锁
情况3:一个对象在两个线程中分别调用一个静态同步方法和一个非静态同步方法
结果:不会产生互斥。
解释:因为虽然是一个对象调用,但是两个方法的锁类型不同,调用的静态方法实际上是类对象在调用,即这两个方法产生的并不是同一个对象锁,因此不会互斥,会并发执行。
情况2:用一个类的静态对象在两个线程中调用静态方法或非静态方法
结果:会产生互斥。
解释:因为是一个对象调用,同上
情况1:用类直接在两个线程中调用两个不同的同步方法
结果:会产生互斥。
解释:因为对静态对象加锁实际上对类(.class)加锁,类对象只有一个,可以理解为任何时候都只有一个空间,里面有N个房间,一把锁,因此房间(同步方法)之间一定是互斥的。
注:上述情况和用单例模式声明一个对象来调用非静态方法的情况是一样的,因为永远就只有这一个对象。所以访问同步方法之间一定是互斥的。
1.对象锁钥匙只能有一把才能互斥,才能保证共享变量的唯一性
2.在静态方法上的锁,和 实例方法上的锁,默认不是同样的,如果同步需要制定两把锁一样。
3.关于同一个类的方法上的锁,来自于调用该方法的对象,如果调用该方法的对象是相同的,那么锁必然相同,否则就不相同。比如 new A().x() 和 new A().x(),对象不同,锁不同,如果A的单利的,就能互斥。
4.静态方法加锁,能和所有其他静态方法加锁的 进行互斥
5.静态方法加锁,和xx.class 锁效果一样,直接属于类的
12 OOM 以及 Stack Overflow 会发生在什么地方
OOM以及Stack Overflow的引用
OOM:
内存溢出: 指当程序需要申请内存的时候却发现没有足够的内存,就会排除该异常。也就是内存溢出。
内存泄漏: 指当程序使用不当,把某一部分弄得不可用。
当在堆中创建了对象,后来没有使用这个对象了,又没有把整个对象的相关引用设为null。此时垃圾收集器会认为这个对象是需要的,就不会清理这部分内存。这就会导致这部分内存不可用。 所以内存泄漏会导致可用的内存减少,进而会导致内存溢出。
方法区溢出
方法区是存放类的信息,而且很难被gc,只要加载了大量类,就有可能引起方法区溢出
这里将不做演示了,想试试的可以用cglib创建大量的代理类
堆溢出
堆是存放对象的地方,那么只要在堆中疯狂的创建对象,那么堆就会发生内存溢出。
//jvm参数:-Xms20m -Xmx20m
public class HeapOOMTest {
public static void main(String[] args){
LinkedList xttblog=new LinkedList();//作为GC Root
while(true){
xttblog.add(new HeapOOMTest());//疯狂创建对象
}
}
}
-Xms20m -Xmx20m作用是将jvm的最小堆容量和最大堆容量都设定为20m,这样就不会动态扩展jvm堆了
这段代码疯狂的创建对象,虽然对象没有声明变量名引用,但是将对象添加到队列l中,这样队列l就持有了一份对象的引用
通过可达性算法(jvm判断对象是否可被收集的算法)分析,队列l作为GC Root,每一个对象都是l的一个可达的节点,所以疯狂创建的对象不会被收集,这就是内存泄漏,这样总有一天堆就溢出了。
运行结果:
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space at java.util.LinkedList.linkLast(Unknown Source) at java.util.LinkedList.add(Unknown Source) at test.HeapOOMTest.main(HeapOOMTest.java:23)
程序发生内存溢出,并提示发生在Java heap space
栈溢出
调用方法的时候,会在栈中入栈一个栈帧,如果当前栈的容量不足,就会发生栈溢出StackOverFlowError
那么只要疯狂的调用方法,并且有意的不让栈帧出栈就可以导致栈溢出了。
//jvm参数:-Xss128k
public class StackSOFTest {
public void stackLeak(){
stackLeak();//递归,疯狂的入栈,有意不让出栈
}
public static void main(String[] args){
StackSOFTest s=new StackSOFTest();
s.stackLeak();
}
}
运行时常量池溢出
这里储存的是一些常量、字面量。如果运行时常量池内存不足,就会发生内存溢出。从jdk1.7开始,运行时常量池移动到了堆中,所以如果堆的内存不足,也会导致运行时常量池内存溢出。
public class RuntimePoolOOM {
public static void main(String[] args){
int i=1;
//保持常量的引用,防止被fullgc收集
LinkedList xttblog=new LinkedList();
while(true){
//将常量添加到常量池
xttblog.add(String.valueOf(i++).intern());
}
}
这里用了链表去保存常量的引用,是因为防止被fullgc清理,因为fullgc会清理掉方法区和老年代
intern()方法是将常量添加到常量池中去,这样运行时常量池一直都在增长,然后内存溢出
工作中也有可能会遇上方法区溢出:
当多个项目都有相同jar包的时候,又都存放在WEB-INF\lib\下,这样每个项目都会加载一遍jar包。会导致方法区中有大量相同类(被不同的类加载器所加载),又不会被gc掉。
解决方案
在应用服务器中建立一个共享lib库,把项目中常用重复的jar包存放在这里,项目从这里加载jar包,这样就会大大减少类加载的数量,方法区也“瘦身”了
如果实在不能瘦身类的话,那可以扩大方法区的容量,给jvm指定参数-XX:MaxPermSize=xxxM
13 分布式下怎么保证接口的幂等性。
冪等性要求多次执行得到的响应一致,同时对实体状态的影响一致
对于一些结果不是要求幂等性的。可以不做处理。
而一些必要的幂等性,都是需要做唯一的标识作区分的
如果对于执行有要求顺序的话,在直接更新之前需要校验此时值和传递过来(要更新的)是否预期一样。
14 GC fullGC 垃圾回收机制
参考 垃圾回收机制
参考 https://www.cnblogs.com/zhguang/p/3257367.html -- JVM
15 jvm加载的双亲委派模式原理
类加载:
jvm把描述类的数据从class文件加载到内存,并对数据进行加载--转换解析--初始化 最终成为jvm可以使用的java类型
类加载包含以下几个流程 加载-->验证-->准备-->解析-->初始化-->使用-->卸载
主动引用与被动引用
主动引用会触发类初始化,被动引用不会触发
类加载器
三种:
(启动类加载器)根加载器(BootStrap classLoader): 加载 JAVA_HOME/lib/rt.jar,或被-Xbootclasspath参数指定路径中的jar包
扩展加载器(Extension classLoader):加载JAVA_HOME/lib/ext包
系统加载器(application classLoader):加载classPath直接路径下的jar包,一般来说,应用程序中的类都与次加载器加载
双亲委派模式加载过程
若一个类加载器收到类加载请求,他首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器去完成,每一层类加载器都是如此,所以所有请求都会传送到顶层的BootStrap ClassLoader,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围没找到),子加载器才会自己尝试去加载