34: 垃圾回收的原理
优势:a.不须要考虑内存管理, b.能够有效的防止内存泄漏,有效的利用可以使用的内存, c.因为有垃圾回收机制,Java中的对象再也不有"做用域"的概念,只有对象的引用才有"做用域"
原理:垃圾回收器是做为一个单独的低级别的线程运行,在不可知的状况下对内存堆中已死亡的或者长期没有使用的对象回收,可是不能实时的对某一对象或者全部对象进行垃圾回收。
垃圾回收机制:分代复制垃圾回收、标记垃圾回收、增量垃圾回收
35: 你写过Java的Web系统
36: 简单介绍一下你的项目
37: 两个有序的数组,合成一个有序的数组,怎么合并效率高
归并排序
38: 淘宝的登录页面,怎么保证他安全
使用哈希加盐法来为密码加密
解决的办法是将密码加密后再存储进数据库,比较经常使用的加密方法是使用哈希函数(Hash Function)。哈希函数的具体定义,你们能够在网上或者相关书籍中查阅到,简单地说,它的特性以下:
(1)原始密码经哈希函数计算后获得一个哈希值
(2)改变原始密码,哈希函数计算出的哈希值也会相应改变
(3) 一样的密码,哈希值也是相同的
(4) 哈希函数是单向、不可逆的。也就是说从哈希值,你没法推算出原始的密码是多少
最简单、常见的破解方式当属字典破解(Dictionary Attack)和暴力破解(Brute Force Attack)方式。这两种方法说白了就是猜密码。
字典破解和暴力破解都是效率比较低的破解方式。若是你知道了数据库中密码的哈希值,你就能够采用一种更高效的破解方式,查表法(Lookup Tables)。还有一些方法,好比逆向查表法(Reverse Look up Tables)、彩虹表(Rainbow Tables)等,都和查表法大同小异。如今咱们来看一下查表法的原理。java
查表法不像字典破解和暴力破解那样猜密码,它首先将一些比较经常使用的密码的哈希值算好,而后创建一张表,固然密码越多,这张表就越大。当你知道某个密码的哈希值时,你只须要在你创建好的表中查找 该哈希值,若是找到了,你就知道对应的密码了。node
从上面的查表法能够看出,即使是将原始密码加密后的哈希值存储在数据库中依然是不够安全的。那么有什么好的办法来解决这个问题呢?答案是加盐。mysql
盐(Salt)是什么?就是一个随机生成的字符串。咱们将盐与原始密码链接(concat)在一块儿(放在前面或后面均可以),而后将concat后的字符串加密。采用这种方式加密密码,查表法就不灵了(由于盐是随机生成的)。 nginx
单单使用哈希函数来为密码加密是不够的,须要为密码加盐来提升安全性,盐的长度不能太短,而且盐的产生应该是随机的。git
39: 你有最新半年用户的订单,天天的用户订单量有上亿,预测下将来一周哪些商品最容易被购买
数据建模-分析
40: 你有啥问题
“入职后有没有培训活动?”
“公司对个人指望是什么?”
“这个部门或团队有多少人?主要是负责哪方面的?”
一、synchronized关键字原理?
原理:synchronized底层是经过一个monitor的对象阻塞和获取。
对代码同步:指令执行时,monitor的进入数减1,若是减1后进入数为0,那线程退出monitor,再也不是这个monitor的全部者。其余被这个monitor阻塞的线程能够尝试去获取这个 monitor 的全部权。
对方法同步:常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,若是设置了,执行线程将先获取monitor,获取成功以后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其余任何线程都没法再得到同一个monitor对象。
重量级锁:Mutex Lock 监视器锁monitor本质就是依赖于底层的操做系统的Mutex Lock来实现的。
二、hashMap底层实现。
首先,HashMap 是 Map 的一个实现类,它表明的是一种键值对的数据存储形式。Key 不容许重复出现,Value 随意。jdk 8 以前,其内部是由数组+链表来实现的,而 jdk 8 对于链表长度超过 8 的链表将转储为红黑树。大体的数据存储形式以下:
从上图中能够看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
源码以下:
Java代码
- /**
- * The table, resized as necessary. Length MUST Always be a power of two.
- */
- transient Entry[] table;
-
- static class Entry<K,V> implements Map.Entry<K,V> {
- final K key;
- V value;
- Entry<K,V> next;
- final int hash;
- ……
- }
能够看出,Entry就是数组中的元素,每一个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。
Java代码
- public V put(K key, V value) {
- // HashMap容许存放null键和null值。
- // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
- if (key == null)
- return putForNullKey(value);
- // 根据key的keyCode从新计算hash值。
- int hash = hash(key.hashCode());
- // 搜索指定hash值在对应table中的索引。
- int i = indexFor(hash, table.length);
- // 若是 i 索引处的 Entry 不为 null,经过循环不断遍历 e 元素的下一个元素。
- for (Entry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- // 若是i索引处的Entry为null,代表此处尚未Entry。
- modCount++;
- // 将key、value添加到i索引处。
- addEntry(hash, key, value, i);
- return null;
- }
当咱们往HashMap中put元素的时候,先根据key的hashCode从新计算hash值,根据hash值获得这个元素在数组中的位置(即下标),若是数组该位置上已经存放有其余元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最早加入的放在链尾。若是数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
采用迭代器遍历,不只适用于HashMap,对其它类型的容器一样适用。
采用这种方法的遍历,能够用下文说起的方式安全地对HashMap内的元素进行修改,并不会对后续的删除操做形成影响。
若是使用foreach遍历方法删除HashMap中的元素,Java颇有可能会在运行时抛出异常。
为何呢?
一、使用iterator迭代删除时没有问题的,在每一次迭代时都会调用hasNext()方法判断是否有下一个,是容许集合中数据增长和减小的。
二、使用forEach删除时,会报错ConcurrentModificationException,由于在forEach遍历时,是不容许map元素进行删除和增长。
因此,遍历删除map集合中元素时,必须使用迭代iterator
若是不是2的2次幂,空间浪费至关大,更糟的是这种状况中,数组能够使用的位置比数组长度小了不少,这意味着进一步增长了碰撞的概率,减慢了查询的效率!而当数组长度为16时,即为2的n次方时,2n-1获得的二进制数的每一个位上的值都为1,这使得在低位上&时,获得的和原hash的低位相同,加之hash(int h)方法对key的hashCode的进一步优化,加入了高位计算,就使得只有相同的hash值的两个值才会被放到数组中的同一个位置上造成链表。
HashMap 包含以下几个构造器:
HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子建立一个 HashMap。
HashMap的基础构造器HashMap(int initialCapacity, float loadFactor)带有两个参数,它们是初始容量initialCapacity和加载因子loadFactor。
initialCapacity:HashMap的最大容量,即为底层数组的长度。
loadFactor:负载因子loadFactor定义为:散列表的实际元素数目(n)/ 散列表的容量(m)。
红黑树本质上是一种二叉查找树,但它在二叉查找树的基础上额外添加了一个标记(颜色),同时具备必定的规则。这些规则使红黑树保证了一种平衡,插入、删除、查找的最坏时间复杂度都为 O(logn)。
三、TCP与UDP的区别
一、基于链接与无链接
二、TCP要求系统资源较多,UDP较少;
三、UDP程序结构较简单
四、流模式(TCP)与数据报模式(UDP);
五、TCP保证数据正确性,UDP可能丢包
六、TCP保证数据顺序,UDP不保证
四、TCP三次握手说一下。
简单说,让双方都证明对方能发收。
知道对方能收是由于收到对方的由于收到而发的回应。
具体:
1:A发,B收, B知道A能发
2:B发,A收, A知道B能发收
3:A发,B收, B知道A能收
五、看你项目用到线程池,说一下线程池工做原理,任务拒接策略有哪几种?
一个线程从被提交(submit)到执行共经历如下流程:
- 线程池判断核心线程池里是的线程是否都在执行任务,若是不是,则建立一个新的工做线程来执行任务。若是核心线程池里的线程都在执行任务,则进入下一个流程
- 线程池判断工做队列是否已满。若是工做队列没有满,则将新提交的任务储存在这个工做队列里。若是工做队列满了,则进入下一个流程。
- 线程池判断其内部线程是否都处于工做状态。若是没有,则建立一个新的工做线程来执行任务。若是已满了,则交给饱和策略来处理这个任务。
任务拒接策略?
有4种内置的实现策略和一个用户自定义拒绝策略。
AbortPolicy 为java线程池默认的阻塞策略,不执行此任务,并且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute须要try catch,不然程序会直接退出。
DiscardPolicy 直接抛弃,任务不执行,空方法 。
DiscardOldestPolicy 从队 列里面抛弃head的一个任务,并再次execute 此task。
CallerRunsPolicy 在调用execute的线程里面执行此command,会阻塞入口 。
用户自定义拒绝策略 实现RejectedExecutionHandler,并本身定义策略模式。
再次须要注意的是,ThreadPoolExecutor.submit() 函数,此方法内部调用的execute方法,并把execute执行完后的结果给返回,但若是任务并无执行的话(被拒绝了),则submit返回的future.get()会一直等到。
future 内部其实仍是一个runnable,并把command给封装了下,当command执行完后,future会返回一个值。
六、进程和线程的区别?
进程和线程的主要差异在于它们是不一样的操做系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不一样执行路径。线程有本身的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,因此多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行而且又要共享某些变量的并发操做,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 另外,进程在执行过程当中拥有独立的内存单元,而多个线程共享内存,从而极大地提升了程序的运行效率。
4) 线程在执行过程当中与进程仍是有区别的。每一个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。可是线程不可以独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分能够同时执行。但操做系统并无将多个线程看作多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
七、ArrayList与LinkedList的区别?
一、ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous)
二、对于随机访问get和set,ArrayList以为优于LinkedList,由于LinkedList要移动指针。
三、对于新增和删除操做add和remove,LinedList比较占优点,由于ArrayList要移动数据。
八、线程安全与非线程安全集合说一下,底层怎么实现的(hashmap,concurrenthashmap)?
Hashmap本质是数组加链表。根据key取得hash值,而后计算出数组下标,若是多个key对应到同一个下标,就用链表串起来,新插入的在前面。
ConcurrentHashMap:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment,默认16个(concurrency level),而后每次操做对一个segment加锁,避免多线程锁的概率,提升并发效率。
九、Hashtable、ConcurrentHashMap、TreeMap、HashMap的key,value都是不为空的吗?
HashMap的key和value都容许为空,treeMap的value容许为空。
十、单例模式
5种
1.饿汉模式(调用效率高,可是不能延时加载):
2.懒汉模式(调用效率不高,可是能延时加载):
3.双重检测锁模式(因为JVM底层模型缘由,偶尔会出问题,不建议使用):
4.静态内部类式(线程安全,调用效率高,能够延时加载):
5.枚举类(线程安全,调用效率高,不能延时加载,能够自然的防止反射和反序列化调用):
如何选用:
-单例对象 占用资源少,不须要延时加载,枚举 好于 饿汉
-单例对象 占用资源多,须要延时加载,静态内部类 好于 懒汉式
不安全 。
double-check 双重检查锁定 。
能保证被它修饰的成员变量能够被多个线程正确的处理。Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另一个线程能读到这个修改的值。它在某些状况下比synchronized的开销更小
十一、 判断一个字符在一个字符串中出现的次数 ?
StringUtils.countMatches(str, t);
十二、HashMap是否是有序的?
不是有序的.
有TreeMap和LinkedHashMap。
- TreeMap和LinkedHashMap是如何保证它的顺序的?
LinkedHashMap 是根据元素增长或者访问的前后顺序进行排序,而 TreeMap是基于元素的固有顺序 (由 Comparator 或者 Comparable 肯定)。
TreeMap TreeMap则实现了 SortedMap 接口。
参照TreeMap的value排序,咱们同样的也能够实现HashMap的排序。
1三、实现全部的线程一块儿等待某个事件的发生,当某个事件发生时,全部线程一块儿开始往下执行的话,有什么好的办法吗?
栅栏(Java的并发包中的CyclicBarrier) CountDownLatch CyclicBarrier Semaphore
- CountDownLatch (N个线程数量count减为0 主程序或一组程序开始执行)
CountDownLatch是一个计数器闭锁,主要的功能就是经过await()方法来阻塞住当前线程,而后等待计数器减小到0了,再唤起这些线程继续执行。
这个类里主要有两个方法,一个是向下减计数器的方法:countdown(),若是取得当前的状态为0,说明这个锁已经结束,直接返回false;
若是没有结束,而后去设置计数器减1,若是compareAndSetState不成功,则继续循环执行。 而其中的一直等待计数器归零的方法是await()。
- CyclicBarrier(N个线程,他们之间任何一个没有完成,全部的线程都必须等待)
CyclicBarrier是加计数方式,计数达到构造方法中参数指定的值时释放全部等待的线程。
- Semaphore(Semaphore 是只容许必定数量的线程同时执行一段任务。)
Semaphore,每次semaphore.acquire(),获取一个资源,每次semaphore.acquire(n),获取n个资源,
当达到semaphore 指定资源数量时就不能再访问线程处于阻塞,必须等其它线程释放资源,semaphore.relase()每次资源一个资源,
semaphore.relase(n)每次资源n个资源。
你知道它的实现原理吗?
继续问,你还知道其它的实现方式吗?
继续问,你以为这些方式里哪一个方式更好?
若是让你来写的话,你以为还有比它更好的实现方式吗?
1四、IO包和NIO包 熟悉吗?
- NIO模型 其中的selector 职责和实现原理
传统的socket IO中,须要为每一个链接建立一个线程,当并发的链接数量很是巨大时,线程所占用的栈内存和CPU线程切换的开销将很是巨大。使用NIO,再也不须要为每一个线程建立单独的线程,能够用一个含有限数量线程的线程池,甚至一个线程来为任意数量的链接服务。因为线程数量小于链接数量,因此每一个线程进行IO操做时就不能阻塞,若是阻塞的话,有些链接就得不处处理,NIO提供了这种非阻塞的能力。
一、增长了一个角色,要有一个专门负责收集客人需求的人。NIO里对应的就是Selector。
二、由阻塞服务方式改成非阻塞服务了,客人吃着的时候服务员不用一直侯在客人旁边了。传统的IO操做,好比read(),当没有数据可读的时候,线程一直阻塞被占用,直到数据到来。NIO中没有数据可读时,read()会当即返回0,线程不会阻塞。
NIO中,客户端建立一个链接后,先要将链接注册到Selector,至关于客人进入餐厅后,告诉前台你要用餐,前台会告诉你你的桌号是几号,而后你就可能到那张桌子坐下了,SelectionKey就是桌号。当某一桌须要服务时,前台就记录哪一桌须要什么服务,好比1号桌要点菜,2号桌要结账,服务员从前台取一条记录,根据记录提供服务,完了再来取下一条。这样服务的时间就被最有效的利用起来了。
Selector类是NIO的核心类,Selector可以检测多个注册的通道上是否有事件发生,若是有事件发生,便获取事件而后针对每一个事件进行相应的响应处理。这样一来,只是用一个单线程就能够管理多个通道,也就是管理多个链接。这样使得只有在链接真正有读写事件发生时,才会调用函数来进行读写,就大大地减小了系统开销,而且没必要为每一个链接都建立一个线程,不用去维护多个线程,而且避免了多线程之间的上下文切换致使的开销。
与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,这2个类构成了服务端处理业务的关键逻辑。
1五、虚拟机JVM 组成部分
程序计数器
|
指示当前程序执行到了哪一行,执行JAVA方法时纪录正在执行的虚拟机字节码指令地址;执行本地方法时,计数器值为undefined
|
虚拟机栈
|
用于执行JAVA方法。栈帧存储局部变量表、操做数栈、动态连接、方法返回地址和一些额外的附加信息。程序执行时栈帧入栈;执行完成后栈帧出栈
|
本地方法栈
|
用于执行本地方法,其它和虚拟机栈相似
|
着重说一下虚拟机栈中的局部变量表,里面存放了三个信息:
- 各类基本数据类型(boolean、byte、char、short、int、float、long、double)
- 对象引用(reference)
- returnAddress地址
这个returnAddress和程序计数器有什么区别?前者是指示JVM的指令执行到哪一行,后者则是你的代码执行到哪一行。
私有内存区伴随着线程的产生而产生,一旦线程停止,私有内存区也会自动消除,所以讨论的内存回收主要是针对共享内存区。
JAVA堆
既然GC主要发生在堆内存中,这部分咱们会对堆内存进行比较详细的描述。
堆内存是由存活和死亡的对象组成的。存活的对象是应用能够访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且尚未被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉以前,他们会一直占据堆内存空间。堆是应用程序在运行期请求操做系统分配给本身的向高地址扩展的数据结构,是不连续的内存区域。用一句话总结堆的做用:程序运行时动态申请某个大小的内存空间。
新生代:刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1。S0和Eden被清空,而后下一轮S0与S1交换角色,如此循环往复。若是对象的复制次数达到16次,该对象就会被送到老年代中。
设置两个Survivor区最大的好处就是解决了碎片化
老年代:若是某个对象经历了几回垃圾回收以后还存活,就会被存放到老年代中。老年代的空间通常比新生代大。
GC名称
|
介绍
|
Minor GC
|
发生在新生代,频率高,速度快(大部分对象活不过一次Minor GC)
|
Major GC
|
发生在老年代,速度慢
|
Full GC
|
清理整个堆空间
|
JAVA 并无给咱们提供明确的代码来标注一块内存并将其回收。或许你会说,咱们能够将相关对象设为 null 或者用 System.gc()。然而,后者将会严重影响代码的性能,由于通常每一次显式的调用 system.gc() 都会中止全部响应,去检查内存中是否有可回收的对象。这会对程序的正常运行形成极大的威胁。另外,调用该方法并不能保证 JVM 当即进行垃圾回收,仅仅是通知 JVM 要进行垃圾回收了,具体回收与否彻底由 JVM 决定。这样作是费力不讨好。
一、追踪回收算法(tracing collector)
从根结点开始遍历对象的应用图。同时标记遍历到的对象。遍历完成后,没有被标记的对象就是目前未被引用,能够被回收。
二、压缩回收算法(Compacting Collector)
把堆中活动的对象集中移动到堆的一端,就会在堆的另外一端流出很大的空闲区域。这种处理简化了消除碎片的工做,但可能带来性能的损失。
三、复制回收算法(Coping Collector)
把堆均分红两个大小相同的区域,只使用其中的一个区域,直到该区域消耗完。此时垃圾回收器终端程序的执行,经过遍历把全部活动的对象复制到另外一个区域,复制过程当中它们是紧挨着布置的,这样也能够达到消除内存碎片的目的。复制结束后程序会继续运行,直到该区域被用完。
可是,这种方法有两个缺陷:
对于指定大小的堆,须要两倍大小的内存空间,
须要中断正在执行的程序,下降了执行效率
四、按代回收算法(Generational Collector)
为何要按代进行回收?这是由于不一样对象生命周期不一样,每次回收都要遍历全部存活对象,对于整个堆内存进行回收无疑浪费了大量时间,对症下药能够提升垃圾回收的效率。主要思路是:把堆分红若搞个子堆,每一个子堆视为一代,算法在运行的过程当中优先收集“年幼”的对象,若是某个对象通过屡次回收仍然“存活”,就移动到高一级的堆,减小对其扫描次数。
串行回收器(serial collector)
并行回收器
CMS回收器
G1回收器
JAVA性能优化
真正影响JAVA程序性能的,就是碎片化。碎片是JAVA堆内存中的空闲空间,多是TLAB剩余空间,也多是被释放掉的具备较长生命周期的小对象占用的空间。
- 减小new对象。每次new对象以后,都要开辟新的内存空间。这些对象不被引用以后,还要回收掉。所以,若是最大限度地合理重用对象,或者使用基本数据类型替代对象,都有助于节省内存;
- 多使用局部变量,减小使用静态变量。局部变量被建立在栈中,存取速度快。静态变量则是在堆内存;
- 避免使用finalize,该方法会给GC增添很大的负担;
- 若是是单线程,尽可能使用非多线程安全的,由于线程安全来自于同步机制,同步机制会下降性能。例如,单线程程序,能使用HashMap,就不要用HashTable。同理,尽可能减小使用synchronized
- 用移位符号替代乘除号。eg:a*8应该写做a<<3
- 对于常常反复使用的对象使用缓存;
- 尽可能使用基本类型而不是包装类型,尽可能使用一维数组而不是二维数组;
- 尽可能使用final修饰符,final表示不可修改,访问效率高
- 单线程状况下(或者是针对于局部变量),字符串尽可能使用StringBuilder,比StringBuffer要快;
- String为何慢?由于String 是不可变的对象, 所以在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,而后将指针指向新的 String 对象。若是不能保证线程安全,尽可能使用StringBuffer来链接字符串。这里须要注意的是,StringBuffer的默认缓存容量是16个字符,若是超过16,apend方法调用私有的expandCapacity()方法,来保证足够的缓存容量。所以,若是能够预设StringBuffer的容量,避免append再去扩展容量。若是能够保证线程安全,就是用StringBuilder。
1六、ArrayList遍历时正确删除元素?
删除元素请使用Iterator方式,若是并发操做,须要对Iterator对象加锁。
1七、对一个List的进行subList后,原有list进行增、删、改,再操做subList会怎样?
子 List 的元素和原 List 中的后一部分是重合的, 而子 List 还在遍历过程当中时, 向原 List 中新增元素, 这样给子 List 的遍历过程形成了干扰甚至困扰, 因而就抛出了并发修改异常将会抛出java.util.ConcurrentModificationException
1八、web应用安全问题?
一、跨站脚本攻击(CSS or XSS, Cross Site Scripting)
方案:输入或输出时对其进行字符过滤或转义处理。
2、SQL注入攻击(SQL injection)
方案:输入输出都是过滤、合法性检查和长度限制等通用方法。
3、远程命令执行(Code execution,我的以为译成代码执行并不确切)
方案:严格限制运行Web服务的用户权限。
4、目录遍历(Directory traversal)
方案:一、一样是限制Web应用在服务器上的运行 2、进行严格的输入验证,控制用户输入非法路径。
5、文件包含(File inclusion)
方案:对文件来源进行审查
6、脚本代码暴露(Script source code disclosure)
7、Http请求头的额外的回车换行符注入(CRLF injection/HTTP response splitting)
8、跨帧脚本攻击(Cross Frame Scripting)
9、PHP代码注入(PHP code injection)
十、XPath injection
十一、Cookie篡改(Cookie manipulation)
十二、URL重定向(URL redirection)
1三、Blind SQL/XPath injection for numeric/String inputs
1四、Google Hacking
1五、表单、AJAX提交必须执行CSRF安全过滤。
1六、URL外部重定向传入的目标地址必须执行白名单过滤。
1九、简单介绍下spring的ioc和aop?
- 控制反转(Inversion of Control,英文缩写为IOC);
ioc就是典型的工厂模式,经过sessionfactory去注入实例。依赖注入 。
本身实现用什么方式? 反射原理
其实就是经过解析xml文件,经过反射建立出咱们所须要的bean,再将这些bean挨个放到集合中,而后对外提供一个getBean()方法,以便咱们得到这bean。
通俗来说就如同婚姻介绍所,只须要告诉它找个什么样的女友,而后婚介就会按照咱们的要求,提供一个mm,若是婚介给咱们的人选不符合要求,咱们就会抛出异常。
- 面向切面编程(Aspect Oriented Programming,英文缩写为AOP)
AOP就是典型的代理模式的体现。 实现拦截器 日志 统一权限校验 。
spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分。
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法建立“方面”,从而使得编译器能够在编译期间织入有关“方面”的代码.简单点解释,比方说你想在你的biz层全部类中都加上一个打印‘你好’的功能,这时就能够用aop思想来作.你先写个类写个类方法,方法经实现打印‘你好’,而后Ioc这个类 ref=“biz.*”让每一个类都注入便可实现。
20、并发问题
丢失更新 用户A把6改为2 用户B把2改为6 则用户A丢失了更新
脏读问题 用户A,B 看到的都是6 用户B把6改成2 则用户A读的值仍然是6
2一、乐观锁 悲观锁
悲观 屏蔽一切违反数据操做完整性
乐观 只是在提交的时候检查是否违反数据完整性
2二、sql优化
- 复杂sql避免模糊匹配
- 索引问题 惟一索引 和普通索引
- 复杂操做
- 在能够使用UNION ALL的语句里,使用了UNION
- 字段长度小于5000用varchar,超过用TEXT,独立一张表,用主键来对应。
- 在varchar字段上创建索引时,必须指定索引长度,不必对全字段创建索引。
2四、介绍下使用的持久层框架? 为何要选择这个(有什么好处)?
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎全部的JDBC代码和参数的手工设置以及结果集的检索。
MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
2五、mybatis $和#的区别
一、效果来看:SELECT * FROM USER WHERE ID = #{id} /${id} ,从效果上来看均可以替换成功,只不过前置默认会把传入的参数当成字符串处理(它还能够经过,jdbcType=Number等指明类型),后者只是简单的替换(不加‘’号)。
二、概念上:${}是一个动态SQL编译,#{}是预编译
三、功能上:#{}由于是预编译,因此能够防止SQL注入,但${}不能。
四、使用上:#{}经常使用来处理参数传入,${}用来处理动态SQL构造,如select * from ${}
五、原则:尽量的使用#{}
PS:
1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,若是传入的值是111,那么解析成sql时的值为order by "111", 若是传入的值是id,则解析成的sql为order by "id".
2. $将传入的数据直接显示生成在sql中。如:order by $user_id$,若是传入的值是111,那么解析成sql时的值为order by user_id, 若是传入的值是id,则解析成的sql为order by id.
3. #方式可以很大程度防止sql注入。
4.$方式没法防止Sql注入。
5.$方式通常用于传入数据库对象,例如传入表名.
6.通常能用#的就别用$.
2六、HashMap和Hashtable有什么区别?
HashMap和Hashtable都实现了Map接口,所以不少特性很是类似。可是,他们有如下不一样点:
HashMap容许键和值是null,而Hashtable不容许键或者值是null。
Hashtable是同步的,而HashMap不是。所以,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
HashMap提供了可供应用迭代的键的集合,所以,HashMap是快速失败的。另外一方面,Hashtable提供了对键的列举(Enumeration)。
通常认为Hashtable是一个遗留的类。
2七、hashCode()和equals()方法的重要性体如今什么地方?
经过hashCode和equals方法保证元素的惟一性,当重写equals方法时,必须重写hashCode方法,由于若是不重写这两个方法,就会默认使用Object的方法,通常是不相同的,因此就会致使存储了重复值,与hashset、hashmap等性质冲突。
2八、Vector、ArrayList和LinkedList有什么区别?
ArrayList和LinkedList都实现了List接口,他们有如下的不一样点:
ArrayList是基于索引的数据接口,它的底层是数组。它能够以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每个元素都和它的前一个和后一个元素连接在一块儿,在这种状况下,查找某个元素的时间复杂度是O(n)。
相对于ArrayList,LinkedList的插入,添加,删除操做速度更快,由于当元素被添加到集合任意位置的时候,不须要像数组那样从新计算大小或者是更新索引。
LinkedList比ArrayList更占内存,由于LinkedList为每个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
也能够参考ArrayList vs. LinkedList。
2九、数据库事务及隔离级别说一下。
原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily),简称就是ACID。
数据库进行任何写入操做的时候都是要先写日志的,一样的道理,咱们在执行事务的时候数据库首先会记录下这个事务的redo操做日志,而后才开始真正操做数据库,
在操做以前,首先会把日志文件写入磁盘,那么当忽然断电的时候,即便操做没有完成,在从新启动数据库时候,数据库会根据当前数据的状况进行undo回滚或者是redo前滚,
这样就保证了数据的强一致性。
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何状况都没法保证。
30、synchronized和lock区别,可重入锁与非可重入锁的区别
- Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
- synchronized在发生异常时,会自动释放线程占有的锁,所以不会致使死锁现象发生;而Lock在发生异常时,若是没有主动经过unLock()去释放锁,则极可能形成死锁现象,所以使用Lock时须要在finally块中释放锁;
- Lock可让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不可以响应中断;
- 经过Lock能够知道有没有成功获取锁,而synchronized却没法办到。
- Lock能够提升多个线程进行读操做的效率。
- 在性能上来讲,若是竞争资源不激烈,二者的性能是差很少的,而当竞争资源很是激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。因此说,在具体使用时要根据适当状况选择。
可重入锁,也叫作递归锁,指的是同一线程 外层函数得到锁以后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁。可重入锁最大的做用是避免死锁。
3一、aop代理模式
AOP 全称 Aspect Oriented Programming,面向切面编程,和 OOP 同样也是一种编程思想。AOP 出现的缘由是为了解决 OOP 在处理 侵入性业务上的不足。
代理模式分为静态代理和动态代理两种。
静态代理:一般用于对原有业务逻辑的扩充。建立一个代理类实现和方法相同的方法,经过让代理类持有真实对象,而后在原代码中调用代理类方法,来达到添加咱们须要业务逻辑的目的。
动态代理:动态代理底层是使用反射实现的,是在程序运行期间动态的建立接口的实现。
3二、jdk1.8新特性
1. 速度更快 – 红黑树
HashMap中的红黑树
HashMap中链长度大于8时采起红黑树的结构存储。
红黑树,除了添加,效率高于链表结构。
2. 代码更少 – Lambda
- Lambda表达式的基础语法:Java8引入了一个新的操做符“->”,该操做符成为箭头操做符或者Lambda操做符,箭头操做符将Lambda表达式拆分红两部分
- 左侧:Lambda表达式的参数列表
- 右侧:Lambda表达式中所需执行的功能,即Lambda体。
3. 强大的Stream API – Stream
一系列流水线式的中间操做。
流是数据渠道,用于操做数据源(集合、数组等)所生成的元素序列。
注意:
①Stream本身不会存储元素。
②Stream不会改变源对象。相反,会返回持有新结果的新Stream。
③Stream操做是延迟执行的。这意味着他们会等到须要结果的时候才执行。
4. 便于并行 – Parallel
在必要的状况下,将一个大任务进行必要的拆分Fork成若干个小任务,再将小任务的运算结果进行Join汇总。
5. 最大化减小空指针异常 – Optional
是一个容器类,表明一个值存在或不存在,原来用null 表示一个值不存在,如今Optional 能够更好的表达这个概念。而且能够避免空指针异常。
六、ConcurrentHashMap
- Jdk1.7时隔壁级别CocnurrentLevel(锁分段机制)默认为16。
- JDK1.8采起了CAS算法
- Jdk1.8没有永久区,取而代之的是MetaSpace元空间,用的是物理内存。
3三、java的4种引用 强软弱虚
强引用
a、new一个对象,强引用不会被GC回收。
b、默认的就是强引用
软引用(SoftReference)、
一、内存不足的时候会被回收
二、会影响对象的回收
弱引用(WeakReference)
一、当引用的对象被回收时,reference的引用为null
二、它不影响对象的回收
弱引用与软引用的区别在于:
只具备弱引用的对象拥有更短暂的生命周期。
在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。
虚引用(PhantomReference)
虚引用主要用来跟踪对象被垃圾回收器回收的活动。
虚引用与软引用和弱引用的一个区别在于:
虚引用必须和引用队列 (ReferenceQueue)联合使用。
当垃圾回收器准备回收一个对象时,若是发现它还有虚引用,就会在回收对象的内存以前,把这个虚引用加入到与之关联的引用队列中
PS:
一、在对应引用A设置成null调用gc时并不会立刻回收,如有对A对象的引用则更不会回收了
二、这些引用会保存着对象设置时的值,因此并非只持有引用。
三、threadlocal中的entity就是使用了weakreference
3四、分布式服务 解决了哪些问题,本身设计一个分布式框架 会用到哪些技术?
须要拆分应用进行服务化,以提升开发效率,调优性能,节省关键竞争资源
当服务愈来愈多时,服务的URL地址信息就会爆炸式增加,配置管理变得很是困难,F5硬件负载均衡器的单点压力也愈来愈大。
当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪一个应用要在哪一个应用以前启动,架构师都不能完整的描述应用的架构关系。
接着,服务的调用量愈来愈大,服务的容量问题就暴露出来,这个服务须要多少机器支撑?何时该加机器?等等…
用到哪些技术: dubbo(分布式框架), zookeeper(开源的分布式协调服务) ,redis(缓存), ssdb, nsq, nginx(负载均衡), Kafka,hessian ,RPC,netty。
3五、Zookeeper服务的注册和发现?
1. init获取Zookeeper的服务注册信息,并缓存在service_repos
2. get_service_repos方法获取实例变量service_repos
3. get_service_endpoint根据init构建好的service_repos,以及lb_strategy提供的负载均衡策略返回某个服务的URL地址
4. update_service_repos经过Zookeeper的Watcher机制来实时更新本地缓存service_repos
5. heartbeat_monitor是一个心跳检测线程,用来进行服务提供者的健康存活检测,若是出现问题,将该服务提供者从该服务的提供者列表中移除;
反之,则加入到服务的提供者列表中LoadBalanceStrategy定义了根据服务提供者的信息返回对应的服务Host和IP,即决定由那台主机+端口来提供服务。
3六、主流的分布式框架?
3七、redis的原理 和存储结构 持久化和非持久
Redis存储机制分红两种Snapshot和AOF。不管是那种机制,Redis都是将数据存储在内存中。
Snapshot工做原理: 是将数据先存储在内存,而后当数据累计达到某些设定的伐值的时候,就会触发一次DUMP操做,将变化的数据一次性写入数据文件(RDB文件)。
AOF 工做原理: 是将数据也是先存在内存,可是在存储的时候会使用调用fsync来完成对本次写操做的日志记录,这个日志揭露文件实际上是一个基于Redis网络交互协议的文本文件。
3八、类加载过程
JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面咱们就分别来看一下这五个过程。
1. 虚拟机在首次加载Java类时,会对静态代码块、静态成员变量、静态方法进行一次初始化(静态间按顺序执行)。
2. 只有在调用new方法时才会建立类的实例。
3. 类实例建立过程:父子继承关系,先父类再子类。父类的静态->子类的静态->父类的初始化块->父类的构造方法->子类的初始化块->子类的构造方法
4. 类实例销毁时候:首先销毁子类部分,再销毁父类部分。
PS:
一、加载:类加载器加载类文件
二、验证:确保class文件结构没有被串改
三、准备:为类的讲台变量分配内存,将其初始化默认值。
四、解析:常量池内的符号引用转为直接引用,将法中对其余方法的应用缓存方法区中对内存地址的引用。
3九、String,StringBuffer,StringBuilder有什么不一样?
一、线程安全方面:stringbuffer是线程安全的,其余都不是。
二、速度:stringbuilder是最快的。
三、内存空间:string每次操做都会建立一个新对象(另string常量池优化),其余都是可变类(有缓存会自增容量)
40、String和StringBuffer的实现?
4一、Volatile关键字做用?除了保证数据可见性,还有其余什么使用方式?
一、做用主要有两个:保持内存的可见性,防止指令重排序
二、保持内存可见性:经过集中原子操做完成工做内存和主内存的交互
a、unlock:做用于主内存,解除独占状态。
b、read:做用主内存,把一个变量的值从主内存传输到线程的工做内存。
c、load:做用于工做内存,把read操做传过来的变量值放入工做内存的变量副本中。
d、use:做用工做内存,把工做内存当中的一个变量值传给执行引擎。
e、assign:做用工做内存,把一个从执行引擎接收到的值赋值给工做内存的变量。
f、store:做用于工做内存的变量,把工做内存的一个变量的值传送到主内存中。
g、write:做用于主内存的变量,把store操做传来的变量的值放入主内存的变量中。
h、lock:做用于主内存,把变量标识为线程独占状态。
valatile的特殊规则:
a、read、load、use动做必须连续出现 :每次读取前必须先从主内存刷新最新的值。
b、assign、store、write动做必须连续出现 : 每次写入后必须当即同步回主内存当中。
三、防止指令重排序:编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理重排序。
a、valitile写操做:storestore 写操做 storeload
b、volatile读操做: loadload 读操做 loadstore
PS:
一、内存屏障:解决指令重排序问题,多CPU的情形下能够强制同步cpu中缓存不一致的状况。
二、缓存一致性原则:
三、锁、内存屏障与一致性
a、只有一个cpu是,cup只会从本身的缓存中读取数据,加入缓存丢失,从主内存读取数据到缓存中,最终内存数据都是一致的。
b、多核状况下,每一个CPU都有本身的缓存,须要读到最新数据就要解决某CPU更新了缓存端但未写回内存,其余cpu看不到的问题。
c、此时就引入了缓存一致性协议(做用域缓存行):
modifiled(该cup已经独占了该缓存段,并作了修改,其余核心读取时,此数据必须刷入主存),
exclusive(数据已经读入cacheline,而且只有该CPU拥有它,能够直接修改数据)
shared(多个cpu共享某内存的数据,当cpu须要修改数据时,须要提交RFO请求获取数据的独占全,即进入exclusive状态才能进行修改)
invalid(share 状态下,当前cpu赞成了其余cpu申请写的时候,变成该状态)
即:当前cpu起始状态(shared),发起rfo,(被接收后当前cpu变成exclusive,其余接收的cpu变成invalid),当前cpu修改了数据就变成modified(这个状态要求读取当前缓存段前,当前数据必须同步到主存)
d、问题:若是当前cpu忙,没接收别的cpu的RFO,则那个发起请求的cpu就无事可作了,这就下降了性能。
e、为了解决上述问题,添加了两个相似缓存的东西,Store buffer和invalidate queue。
这样当cpu须要写缓存行时就将写指令丢入storebuff,去干别的事,等RFO受到回应时,该指令才执行。
cup收到其余核心的RFO指令后,会当即回应但相应的失效操做指令(较忙时)会放入到invalidate queue中
f、这套机制实现了异步,带了性能提高的同时也带来了问题,在发去RFO请求的cpu执行写时(先读store buffer,再读缓存)只对当前CPU可见(其余cpu可能没有执行本身invalidate queue中的失效指令)。
g、为了解决这个问题,引入了读写屏障。写屏障保证写屏障前在全部store buffer的指令都真正的写入到缓存,读屏障保证在读屏障前全部invalidate queue中全部的无效化指令都执行。这保证了不一样核心上,缓存的强同步。
h、在锁的实现上,通常lock都会加入读屏障,保证后续代码能够读到别的cpu未回写的缓存数据(
应该是作缓存失效吧?),而unlock会加入写屏障,将全部未回写的缓存回写(
确保到modified状态吧)。
四、内存屏障保证缓存的一致性
详参:http://gocode.cc/project/9/article/128
4二、ThreadLocal作什么的?如何使用?源码是如何实现的?get()方法?
一、并发背景下,经过将相关对象封闭到执行线程中来解决并发问题,是一种以空间换时间的作法。
二、具体实现就是为当前执行线程维护一个ThreadLocalMap对象(维护到执行线程中,每一个线程建立一个),其中维护一个entity对象数组(继承weakReference),其中threadlocal对象为key,设置的对象为value,以实现thread中维护多个threadLocal对象
三、通常在类中使用final static 修饰
四、get方法使用当前threadlocal对象实例从当前执行线程的threadlocalmap中获取存放的对象。
五、线程的threadLocalMap中之索引维护一个entity数组,是由于一个线程能够持有多个threadlocal
PS:
一、threadLocalMap中维护的entity继承了weakReference,功能就是当threadlocal被回收后就变成null->value
二、threadLocal在get时会清除key为null的entity
4三、ConcurrentHashMap源码?JDK1.6,1.7,1.8中分别有什么不一样?
一、负载因子0.75,默认容量16,当大于16*0.75时扩容一倍。
二、1.6和1.7无太大差异,只是在new hashmap时1.6确实开辟了内存空间,1.7采用的是懒汉式,在put时才构造。
三、1.7对待并发采用的是基于分片+链表数组结构,要通过两次hash碰撞,分片是用的是可重入锁;1.8则摒弃了分片直接使用synchronized加到桶中第一个元素上,cas用于交换元素。
四、1.8对待增加,当链表长度大于8时但桶大小小于64时扩充容量,大于64时将桶转为红黑树操做。
PS:
一、node中的value和next都用volatile修饰。
二、hash碰撞就是两个对象的key的hashcode同样,这时候如何获取他的value。
三、1.8中的spread方法对hash作了扩展,将高16位和当前hash作异或操做,解决选择桶的下标时老是与低4位运算,形成的表的长度较小问题,从而减小系统的开销(hash碰撞状况)
四、桶的大小超过64时,使用红黑树也是当发生较大碰撞时下降冲突的考虑。
4四、分布式程序调用链
全链路性能监控从总体维度到局部维度展现各项指标,将跨应用的全部调用链性能信息集中展示,可方便度量总体和局部性能,而且方便找到故障产生的源头,生产上可极大缩短故障排除时间。
Google Dapper
一、背景:随着微服务的应用,业务调用链愈来愈复杂,一个请求可能涉及到几十个服务的系统服务,涉及到多个团队的业务系统。当遇到问题须要定位时,也会产生一系列麻烦。
二、解决方案:经过调用链,把一次请求调用过程串联起来,实现对请求路径的监控,便于快速定位。
三、调用链显示内容:各个调用环节的性能分析(如各API使用时间、使用堆栈)、调用个环节依赖关系还原、SQL打印、IP显示。
四、通用框架:google的Dapper,淘宝的鹰眼,京东的九头蛇。
五、调用链原理:
a、请求生成一个全局TranceId,经过TraceId能够串联起整个调用链,一个tranceId表明一次请求。
b、除了TranceId,还须要SpanId记录调用的父子关系,span是本身生成,透传子调用成为parentId
c、一个没有parentId的span是调用链入口
e、这个调用过程当中每一个请求都要透传tranceId和spanId
f、要查看某次完整的调用链只要根据TranceId查出全部调用记录,而后经过parentId和spanId组织起整个调用父子关系。
PS:具体参见
https://blog.csdn.net/Damon__Wang/article/details/81782911
https://blog.csdn.net/Damon__Wang/article/details/82051631
4五、生产环境如何定位内存溢出?CPU使用率太高?Linux命令?
一、内存溢出的常见状况分几种:堆溢出(java heap space),PermGen space(方法区),不能建立本地线程(unable to create new native thread),回收执行了太长时间、超过限制(GC overhead limit exceeded)。初步判断区域
二、关键还在于分析dump文件。这个能够提早设置+HeapDumpOnOutOfMemeryError,或jmp
三、使用Jprofile打开dump文件,这里能够看到大对象和具体的引用
四、固然结合gc的日志更好(-XX:+PrintGC)
4六、Netty
Netty 是一个基于NIO的客户、服务器端编程框架。
Netty是什么?
1)本质:JBoss作的一个Jar包
2)目的:快速开发高性能、高可靠性的网络服务器和客户端程序
3)优势:提供异步的、事件驱动的网络应用程序框架和工具
4)特色:
a、并发高
b、传输快:领拷贝,使用直接缓冲区
c、封装好:比较简洁,使用链式调用
通俗的说:一个好使的处理Socket的东东
4七、kafka 事务 性能
4八、内存屏障
Java内存模型中volatile变量在写操做以后会插入一个store屏障,在读操做以前会插入一个load屏障。一个类的final字段会在初始化后插入一个store屏障,来确保final字段在构造函数初始化完成并可被使用时可见。
PS:
一、为了提升性能,处理器设计了多级缓存,cpu的缓存和共享的缓存。cpu把处理结果发到缓存后就能够作其余处理了,但这也形成了可见性问题。
二、缓存操做是分红缓存行,缓存一致性原则(MESI)经过定义独占、共享、修改、失效等缓存行的状态来协调多个处理器对其的操做。(内存级别)当共享的同一缓存端的数据发生变化时其余cpu都会获得通知。
三、为了杜绝内存不一致的状况(如指令重排序,cpu和编译阶段都会),又引入了内存屏障来确保一致性。内存屏障分为LoadLoad屏障,LoadStore屏障,StoreStore屏障,StoreLoad屏障,都是确保后一个操做前前一个操做必须完成(指令在中间,分割对应的操做指令,肯定先后的一个关系)。(指令级别)
四、lock前缀指令具有内存屏障功能(load&store)的的CUP指令,执行时锁住子系统来确保执行顺序,甚至跨多个CPU。
五、JVM中,除了内存屏障,还使用先行发生原则来确保指令的先后关系。如对象锁释放必须先于加锁发生,start先于thread内全部指令执行前发生。
六、cas操做能够理解为是lock指令(锁着内存)+系统cas指令来实现的。
七、
4九、redis面试题
一、Redis有哪些数据结构?程序员
a、基本类型:字符串,数值
b、集合:字典Hash、列表List、集合Set、有序集合SortedSet,链表
c、其余:若是你是Redis中高级用户,还须要加上下面几种数据结构HyperLogLog、Geo、 Pub/Sub。Redis Module,像BloomFilter,RedisSearch,Redis-ML。
二、使用过Redis分布式锁么,它是什么回事?github
a、先拿setnx来争抢锁,抢到以后,再用expire给锁加一个过时时间防止锁忘记了释放。
三、若是在setnx以后执行expire以前进程意外crash或者要重启维护了,那会怎么样?web
a、这个锁就永远得不到释放了。
b、set指令有很是复杂的参数,这个应该是能够同时把setnx和expire合成一条指令来用的! 如redis的lua脚本
四、假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,若是将它们所有找出来?
a、使用keys指令能够扫出指定模式的key列表。
五、若是这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
a、redis关键的一个特性:redis的单线程的。keys指令会致使线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。
b、这个时候能够使用scan指令,scan指令能够无阻塞的提取出指定模式的key列表,可是会有必定的重复几率,
c、在客户端作一次去重就能够了,可是总体所花费的时间会比直接用keys指令长。
六、使用过Redis作异步队列么,你是怎么用的?
a、通常使用list结构做为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
七、可不能够不用sleep呢?
a、list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
八、能不能生产一次消费屡次呢?
a、使用pub/sub主题订阅者模式,能够实现1:N的消息队列。publish/subscribe
九、pub/sub有什么缺点?
a、在消费者下线的状况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。(消息是即时的,不作存储)
十、redis如何实现延时队列?
a、使用sortedset和string,数据做为拿时间戳做为score,消息内容做为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒以前的数据轮询进行处理。
b、string中uuid做为key,data做为value存放数据。
c、sortedset中,string中的uuid做为key,时间戳做为value存储。获取的时候使用zrangebyscore排序回去最先的数据。
d、应对的场景就是:并发对数据库更新时,锁表会致使效率低,使用这种延迟操做能够解决这种效率低的问题。
十一、若是有大量的key须要设置同一时间过时,通常须要注意什么?
a、若是大量的key过时时间设置的过于集中,到过时的那个时间点,redis可能会出现短暂的卡顿现象。
b、通常须要在时间上加一个随机值,使得过时时间分散一些。(过时时间=固定时间+随机值)
十二、Redis如何作持久化的?
a、bgsave作镜像全量持久化,aof作增量持久化。
b、由于bgsave会耗费较长时间,不够实时,在停机的时候会致使大量丢失数据,因此须要aof来配合使用。
c、 在redis实例重启时,会使用bgsave持久化文件从新构建内存,再使用aof重放近期的操做指令来实现完整恢复重启以前的状态。
1三、若是忽然机器掉电会怎样?
a、取决于aof日志sync属性的配置,若是不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。
b、可是在高性能的要求下每次都sync是不现实的,通常都使用定时sync,好比1s1次,这个时候最多就会丢失1s的数据。
1四、bgsave的原理是什么?
a、fork和cow。fork是指redis经过建立子进程来进行bgsave操做,cow指的是copy on write,
b、子进程建立后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。
1五、Pipeline有什么好处,为何要用pipeline?
a、能够将屡次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。
b、使用redis-benchmark进行压测的时候能够发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。
1六、Redis的同步机制了解么?
a、Redis能够使用主从同步,从从同步。
b、第一次同步时,主节点作一次bgsave,并同时将后续修改操做记录到内存buffer,待完成后将rdb文件全量同步到复制节点, 复制节点接受完成后将rdb镜像加载到内存。
c、加载完成后,再通知主节点将期间修改的操做记录同步到复制节点进行重放就完成了同步过程
1七、是否使用过Redis集群,集群的原理是什么?
a、Redis Sentinal着眼于高可用,在master宕机时会自动将slave提高为master,继续提供服务。
b、 Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
50、动态代理有几种,Jdk与Cglib区别
一、实现方式:
a、jdk经过反射机制生成一个实现代理接口(参数中interfaces里全部接口且继承了Proxy的代理类)的匿名类,在调用具体方法前调用invokeHandler的invoke处理
a.1,Proxy.newProxyInstance(classloader,target.getclass.getInterfaces(),target(implemants InvocationHandler)
a.二、生成一个实现了参数中interfaces里全部接口且继承了Proxy的代理类的字节码,而后用参数中的classloader加载这个代理类。
a.三、使用代理类父类的构造函数Proxy(invocationHandler)来建立一个代理类实例,将咱们自定义的InvocationHandler的子类传入。
a.四、返回这个代理类的实例。
b、cglib利用ASM开源包,直接修改代理类class的字节码生成子类来重写其方法。
二、目标类的限制:
a、jdk只能正对实现了接口的类
b、cglib针对有误实现接口的都行
c、cglib不能重写final类或方法
三、性能:jdk是愈来愈快
ps:
一、Java 字节码操控框架。它能被用来动态生成类或者加强既有类的功能
二、spring中的应用:<aop:aspectj-autoproxy proxy-target-class="true"/>:cglib
5一、数据库三大范式
一、字段不能够再分:也就是说一个列名下的值只能是一种类型,如号码中既有手机号、座机号等都不行
保证字段的原子性,也是关系型数据库的标准(面向对象)
二、有主键,非主键字段依赖主键(有一个主题):一个主键表明一条记录,只能有一个主题;也就是说一条记录中不能即存在学生的记录信息也存在课程的主题信息
惟一性
三、非主键字段不能相互依赖(都是平级的):每列都与主键有直接关系,不存在传递依赖。
5二、左链接和右链接说一下,内链接呢
一、左链接:使用left join on,匹配时匹配表中没能匹配上的也显示,驱动表中只显示匹配的上的
二、右链接:使用right join on,效果和左链接相反
三、内连接:使用(inner) join on ,匹配表和驱动表都只显示匹配的上的。
四、优化:都须要优化驱动表
五、性能:左右关联比内关联要好一点。
5三、数据库索引有几种
5四、数据库引擎你认识几种,innodb 和myisam 区别,你的项目用到哪一个引擎
PS:
一、指数据库事务正确执行的四个基本要素:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
二、数据库引擎是用于存储、处理和保护数据的核心服务。利用数据库引擎可控制访问权限并快速处理事务,从而知足企业内大多数须要处理大量数据的应用程序的要求。
三、查询数据库支持的引擎:show engines
四、查询数据库使用的引擎:show variables like '%storage_engine%';
5五、若hashcode方法永远返回1会产生什么结果
一、首先、编程时就坚持修改了equals就要修改hashchode要求
二、hashcode在对象的对比和一些集合类中会被用到,如hashmap
三、hashcode的存在也是一种优化程序的体现,如hashmap中的桶,若是全部的hashcode相同,那hashcode会构成一个线性表,致使性能降级
PS:为何选择31作乘数
一、不大不小:为了不hash重复须要选择一个大一点的质数作乘数,不然就会致使hash值重复的较多,100以上的数乘下来容易超过int的范围。
二、31,33,37,39,41中31能够被jvm优化,做为位移计算,这种很高效。
5六、Error与RuntimeException的区别
两者的不一样之处:
Exception:
1.能够是可被控制(checked) 或不可控制的(unchecked)
2.表示一个由程序员致使的错误
3.应该在应用程序级被处理
Error:
1.老是不可控制的(unchecked)
2.常常用来用于表示系统错误或低层资源的错误
3.如何可能的话,应该在系统级被捕捉
5七、引用计数法与GC Root可达性分析法区别
一、引用计数:相似给对象添加一个计数器,当对象被引用的时候就在本身的计数器上加一,当某个引用的对象被回收后。引用为零的对象就会被回收
优势:简单、高效
缺点:相互引用不能被识别(a.instance=c,b.instance=a,这两项都再也不被使用,但却没法回收)
二、可达性分析:从根开始遍历他的引用,当某个对象到根不可达时则对象该引用能够回收
根节点:方法区的常量、静态变量
虚拟机栈区的变量列表的引用
本地方法栈中的引用
本地方法栈中引用的对象
PS:
一、五大分区:
a、程序计数器:记录当前执行程序的位置,改变计数器的值来肯定执行下一条指令,如循环、分支、方法跳转
b、虚拟机栈:每一个线程都会建立一个虚拟机栈,经过压栈出栈的方式执行方法调用。分局部变量表、操做数栈、动态连接、方法出口等。
c、本地方法栈:native方法
d、堆:存放对象实例
e、方法区:用于存放已被虚拟机加载的类信息,常量,静态变量等数据。
f、直接内存:并非虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域。
二、各区域的使用

5八、双亲委派机制说一下
一、某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,若是父类加载器能够完成类加载任务,就成功返回;只有父类加载器没法完成此加载任务时,才本身去加载。
二、全盘责任委托:一个类若是被某个类加载器加载,那么除非指定别的加载器,不然这个类关联的类也有这个类加载器加载。
三、由下向上询问是否加载,由上向下尝试加载。
四、线程上下文加载器:针对java的spi状况,也就是引导类加载的类型须要使用二方包的状况,如jdk
5九、算法题:找出一个数组中第100个小的数字(堆思想解决)
60、看你项目用到策略模式和工厂模式,说一下区别
一、比喻:去必胜客吃披萨,工厂模式关注的是最终能吃到披萨,策略模式关注在关注的是披萨是如何作的。
a、用途不一样
工厂是建立型模型,他的做用是建立对象
策略是行为型模型,他的做用是让一个对象在许多行为中选择一种行为。
b、关注点不一样
一个关注对象的建立
一个关注行为的封装
c、解决不一样的问题
工厂模式,它接受指令,建立出符合要求的实例。它主要解决的是资源的统一分配,将对象的建立独立出来,让对象的建立和具体的使用客户无关。
策略模式,它为了解决策略的切换与扩展,让策略模式的变化独立于使用策略模式的用户。
d、工厂至关于黑盒子,策略至关于白盒子
PS:
一、
设计模式有三种类型:建立型(解决:对象的建立和具体的使用解耦),行为型(描述了对象和类的模式,以及它们之间的通讯模式)
,组合型(解决怎样组装现有的类,设计他们的交互方式,从而达到实现必定的功能的目的)。
二、项目中:缓存的使用,定义一个公用的缓存操做页面,在缓存工厂中经过配置缓存类别获取具体的缓存实现,redis,tair
三、简述:


四、工厂模式有三种:简单工厂,工厂方法,抽象工厂。
a、简单工厂(静态工厂):将类的实例化转交一个工厂,具体的行为由子类决定(即如何构造该实例);解决的问题:怎么构造一个对象有工厂内部决定,如是否设置某个属性等,用于隐藏实现细节。如计算器,获取单例等
b、工厂方法:定义一个用于建立对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。不一样的对象使用不一样的工厂
c、抽象工厂:提供一个建立一系列相关或相互依赖对象的接口,而无需指定他们具体的类。解决的问题:切换数据库
五、策略模式:它定义了算法家族,分别封装起来,让他们之间能够相互替换,此模式让算法的变化,不会影响到使用算法的客户
6一、模板方法模式
定义:一个模板结构,将具体内容延迟到子类去实现。
解决的问题:
一、将复用性高的代码抽取到抽象父类中,具体的操做在继承的子类中定义。
二、父类调用子类操做,子类扩展不一样的行为,这样即实现了控制反转也符合开闭原则。
使用场景
试卷,apache velocity
6二、开闭原则懂吗,说一下
一、设计模式中提到的一个概念。
二、目的指导咱们如何创建一个稳定的、灵活的系统。
三、开闭原则的定义是:软件中的对象(类、模块、函数等)应该对于扩展是开放的,可是,对于修改是封闭的。(能够新增、尽量少的修改)
6三、NIO说一下你的理解
一、NIO也就是非阻塞IO,相对于BIO而言,固然也有AIO
二、打个比喻水工接水的比方:BIO就是多个多个水笼统,每一个水龙头都有一个接水工,每一个接水工只有接到水才才会作后续处理;NIO是为每一个水龙头添加了一个水缸且只有一个水工负责查看接到水刚,当某个水缸接满水,水工就先处理这个水缸的后续处理。
三、这个水缸就缓冲区buffer,水工就是选择器Select,水工就看查看某个水龙头时就等于链接了通道channel。
四、具体的实现时,全部的通道都注册到选择器中,选择器轮循查看通道中的数据是否准备就绪,而读出和写入都是直接到了缓冲区。
五、这个地方提供了一个新的概念:直接缓冲区,绕过内核地址空间(系统空间),直接将数据的物理地址映射到用户地址空间(JVM)。
ps:以前的数据拷贝都是先写到物理内存,而后再拷贝到jvm中。
6四、AtomicInteger底层原理
一、AtomicInteger是一个提供原子操做的Integer类,经过线程安全的方式操做加减。
二、其中存储的value使用了volatile修饰,操做时使用cas无锁算法。
ps:
一、具体实现就是使用Unsafe。
二、它有以下功能:内存管理(分配、释放内存),很是规的对象实例化(无需调用构造器),操做类、对象、变量(指针偏移获取),数组操做(指针偏移),多线程同步(对象锁机制,cas操做),线程挂起与恢复,内存屏障(loadFence、storeFence、fullFence)
6五、CAS机制是什么?有什么缺点,会出现什么问题?
CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
CAS机制当中使用了3个基本操做数:内存地址V,旧的预期值A,要修改的新值B,若预期值A和内存值V相同就把内存值修改为新值B
CAS的缺点:
1.CPU开销较大
在并发量比较高的状况下,若是许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
2.不能保证代码块的原子性
CAS机制所保证的只是一个变量的原子性操做,而不能保证整个代码块的原子性。好比须要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。
3.ABA问题
这是CAS机制最大的问题所在。
PS:
什么是ABA问题?
引用原书的话:若是在算法中的节点能够被循环使用,那么在使用“比较并交换”指令就可能出现这种问题,在CAS操做中将判断“V的值是否仍然为A?”,而且若是是的话就继续执行更新操做,在某些算法中,若是V的值首先由A变为B,再由B变为A,那么CAS将会操做成功。
Java中提供了
java.util.concurrent.atomic中AtomicStampedReference和AtomicMarkableReference来解决ABA问题。
多CPU的状况下的cas操做是CPU提供的支持。
一、这和volatile的底层实现是相同的
二、底层:这个读取、对比以及设置的操做私用lock前缀保证惟一性。
6六、本地缓存过时策略怎么设置,一致性怎么保证?
1、一致性(以下是主动的状况)
一、当数据时效性要求比较高时,须要保证缓存与数据库保存一致,并且须要保证缓存节点和副本中的数据也要保存一致,不能出现差别现象。
二、这就比较依赖缓存过时和更新策略,通常会在数据发生更改时,主动更新缓存中的数据或者移除对应的缓存。
三、通常的缓存使用
方案一(先更新缓存,再更新数据库):
一、是不可用的
二、首先库存是以数据库为准的,若是缓存更新完成但数据库未更新完成且库存少于缓存则会形成负库存。
三、若缓存更新成功,数据库更新失败则缓存一直都是脏数据。
方案二(新更新数据库,再更新缓存)
一、不可取
二、若是两个线程并发执行,会存在A更新数据库,B更新数据库,B更新缓存,A更新缓存(网络缘由),则数据库中的就是脏数据。
三、针对那种依赖前值计算后更新的场景,无疑是浪费性能。
方案三(先删除缓存,再更新数据库:更新数据库失败对业务也没什么影响)
一、待优化
二、存在A删除了缓存,B发现缓存不存在从数据库查询到旧值写到了缓存,A将新值写入到数据库:此时缓存和数据库不一致。
三、能够采用延时双删策略:先淘汰缓存,再写入数据库,休眠1秒,再次淘汰缓存。此时能够确保上述B的写入被删除。(休眠是为了确保读请求结束,写请求能够删除对请求形成的脏数据)
四、第二次删除能够采用新线程来作以免下降吞吐。
五、第二次删除失败了的解决方案详见方案四
方案四(先更新数据库,再删除缓存)
一、待优化
二、缓存恰好失效,A查询数据库获得旧值,b将新值写入数据库并删除缓存,A将旧值写入缓存(若b的写入数据库操做要足够端以致于B删除缓存早于A写入缓存发生:几率低)
三、2的解决方案:异步延时删除,
缓存设置有效时间。
四、针对缓存更新失败的解决方案:
删除失败后,将删除key的消息发送到消息队列,重试删除直到成功(这对业务代码有侵入)

使用mysql的中间件如Canal,单启一个独立的程序去处理。
2、过时策略
一、缓存过时策略大体分两种:能够经过过时时间来控制内容过时的状况和没法经过过时时间来控制内容过时的状况。
二、能够经过过时时间来控制内容过时的状况
a、设置绝对过时时间(秒杀商品)。
三、没法经过过时时间来控制过时的状况
a、设置滑动过时(针对时效性不强的):在读取缓存的时候将该缓存项的过时时间在当前时间的基础上延后指定长度的事件。(如文章有评论就延长过时时间的状况)
ps:
一、缓存满了,从缓存中移除数据的策略
a、新进先出算法
b、最久未使用算法
c、最少使用算法
二、缓存并发问题
a、缓存过时后将尝试从后端数据库获取数据,当数据获取到更新完成这段会有多个线程到数据库后去数据,对数据库形成极大的冲击,甚至致使血崩。
b、此时就要加锁,到后台数据库请求数据要先尝试获取锁,未获取锁的线程只能等待。
c、针对缓存过时也要选择一个范围内随机过时,不能所有集中到某个时间段。
三、缓存穿透(不存在的key,缓存不起做用)
a、查询一个不存在的数据,因为缓存是不被命中被动写的(若是没有就查数据库),而且出于容错考虑,若是存储层查不到数据就不写入缓存,这将致使每次都要查询存储层查询,失去了缓存的意义。在流量大的时候,可能db就挂掉了。
b、缓存空对象,过时时间段,不超过五分钟。
c、单独过滤处理:将对应数据为空的key进行统一存放
d、布隆过滤器:将全部可能存在的数据哈希到一个足够大的bitmap中,一个必定不存在的数据会被 这个bitmap拦截掉
四、缓存颠簸:缓存节点故障致使,使用hash算法解决
五、缓存雪崩
a、缓存采用了相同的过时时间,致使缓存再同一时刻同时失效,DB瞬间压力过大崩溃。
b、在缓存失效的基础上添加1-5分钟的随机值
c、从应用架构角度,咱们能够经过限流、降级、熔断等手段来下降影响,也能够经过多级缓存来避免这种灾难
六、缓存击穿(一个存在的key,在缓存过时的一刻,同时有大量的请求)
a、使用互斥锁:使用setnx设置值,成功消息返回的才去查询数据库(成功后设置有效期)
七、缓存无底洞现象
八、其余
a、目前主流的数据库、缓存、Nosql、搜索中间件等技术栈中,都支持“分片”技术,来知足“高性能、高并发、高可用、可扩展”等要求
b、命中:能够直接经过缓存获取到须要的数据。
3、cache的使用
4、掌医的实现
5、商品秒杀逻辑