一、 说一下几种常见的排序算法和分别的复杂度
冒泡排序 时间复杂度是O(n2) 选择排序 复杂度是O(n2 ) 插入排序 时间复杂度是O(n2) 堆排序 时间复杂度O(nlog n) 归并排序 O(nlog2n) 快速排序 最理想情况算法时间复杂度O(nlog2n),最坏O(n ^2)
二、 用Java写一个冒泡排序算法
public static void main(String[] args) {
// TODO Auto-generated method stub
int arr [ ] ={1,6,0,-1,9};
int temp=0;//中间值
//———-冒泡排序法
//外层循环,它决定一共走几趟
for(int i = 0;i
//如果我们发现前一个数比后一个数大,则交换
for(int j=0;j
//换位
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
//输出结果
for(int i = 0;i
}
三、 描述一下链式存储结构。
链式存储结构,又叫链接存储结构。在计算机中用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的). 一般在计算机的硬盘中,文件都是链式存储的。我们知道,多个扇区组成一个簇,簇是计算机存储数据的基本单位。而一个文件是存储在多个在空间上也许并不相连的簇中的。这就是链式存储。但是为了能够读取出这个文件,计算机会在该文件第一部分的尾部写上第二部分所在的簇号。第二部分的尾部又写上第三部分,以此类推,最后一部分写上一段代码,表示这是该文件的最后一部分。值得一提的是,高簇号在后。(如代码所示的1234实为簇3412)文件所占簇可认为是随机分配的。
特点:1、比顺序存储结构的存储密度小(链式存储结构中每个结点都由数据域与指针域两部分组成,相比顺序存储结构增加了存储空间)。
2、逻辑上相邻的节点物理上不必相邻。
3、插入、删除灵活 (不必移动节点,只要改变节点中的指针)。
4、查找节点时链式存储要比顺序存储慢。
5、每个节点是由数据域和指针域组成。
6、由于簇是随机分配的,这也使数据删除后覆盖几率降低,恢复可能提高。
四、如何遍历一棵二叉树?
前序遍历:按照“根左右”,先遍历根节点,再遍历左子树 ,再遍历右子树
中序遍历:按照“左根右“,先遍历左子树,再遍历根节点,最后遍历右子树
后续遍历:按照“左右根”,先遍历左子树,再遍历右子树,最后遍历根节点
五、 倒排一个LinkedList
class Node{
char value;
Node next;
}
//非递归实现
public Node reverse(Node current) {
Node previous = null;
Node next = null;
while (current != null) {
//存储下一节点
next = current.next;
current.next = previous; //反转
//更新遍历节点
previous = current;
current = next;
}
return current;
}
递归方式:
假设1~n-1已经全部完成,那么1 -> 2 -> 3 -> 4 ———> 3-> 2 -> 1 -> null 和 4。因此,我们就把4加在3前面即可。
corner case:和上一样
code:
public ListNode reverseLinkedList(ListNode root) {
if (root == null || root.next == null) return root;
ListNode cur = root;
while (cur.next != null) {
cur = cur.next;
}
helper(root, root.next);
return cur;
}
public void helper(ListNode first, ListNode second) {
if (second == null) return first;
helper(second, second.next);
first.next = second.next;
second.next = first;
}
六、 用Java写一个递归遍历目录下面的所有文件
public class Counter2 {
public static void main(String[] args) {
//取得目标目录
File file = new File(“D:”);
//获取目录下子文件及子文件夹
File[] files = file.listFiles();
readfile(files);
}
public static void readfile(File[] files) {
if (files == null) {// 如果目录为空,直接退出
return;
}
for(File f:files) {
//如果是文件,直接输出名字
if(f.isFile()) {
System.out.println(f.getName());
}
//如果是文件夹,递归调用
else if(f.isDirectory()) {
readfile(f.listFiles());
}
}
}
}
七、 接口与抽象类的区别?
八、 Java中的异常有哪几类?分别怎么使用?
Exception又包含了运行时异常(RuntimeException, 又叫非检查异常)和非运行时异常(又叫检查异常)
(1) Error是程序无法处理了, 如果OutOfMemoryError、OutOfMemoryError等等, 这些异常发生时, java虚拟机一般会终止线程 .
(2) 运行时异常都是RuntimeException类及其子类,如 NullPointerException、IndexOutOfBoundsException等, 这些异常是不检查的异常, 是在程序运行的时候可能会发生的, 所以程序可以捕捉, 也可以不捕捉. 这些错误一般是由程序的逻辑错误引起的, 程序应该从逻辑角度去尽量避免.
(3) 检查异常是运行时异常以外的异常, 也是Exception及其子类, 这些异常从程序的角度来说是必须经过捕捉检查处理的, 否则不能通过编译. 如IOException、SQLException等
九、常用的集合类有哪些?比如List如何排序
常见的集合接口Set、List、Map等,其中Set和List继承自Collection
sort()方法排序的本质其实也是借助Comparable接口和Comparator接口的实现,一般有2种用法:1、直接将需要排序的list作为参数传入,此时list中的对象必须实现了Comparable接口,然后sort会按升序的形式对元素进行排序;2、传入list作为第一个参数,同时追加一个Comparator的实现类作为第二个参数,然后sort方法会根据Comparator接口的实现类的逻辑,按升序进行排序;
这里可能有朋友会问,为什么都是升序没有降序。
其实所谓升或者降都是你自己规定的,sort()方法只是将比较结果为-1的放前面,0的放中间,1的放后面;如果你想实现降序排列,那就在Comparator方法的实现类中,逆转compare的返回结果就行了。
十、 ArrayList和LinkedList内部的实现大致是怎样的?他们之间的区别和优缺点?
1.ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。2对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。
优缺点有:
1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对 ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是 统一的,分配一个内部Entry对象。2.在ArrayList集合中添加或者删除一个元素时,当前的列表所所有的元素都会被移动。而LinkedList集合中添加或者删除一个元素的开销是固定的。3.LinkedList集合不支持 高效的随机随机访问(RandomAccess),因为可能产生二次项的行为。4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
所以在我们进行对元素的增删查操作的时候,进行查操作时用ArrayList,进行增删操作的时候最好用LinkedList。
十一、内存溢出是怎么回事?请举一个例子?
1、OutOfMemoryError: PermGen space
Permanent Generation space 这个区域主要用来保存加来的Class的一些信息,在程序运行期间属于永久占用的,Java的GC不会对他进行释放,所以如果启动的程序加载的信息比较大,超出了这个空间的大小,就会发生溢出错误
解决的办法无非就是增加空间分配了——增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。
2、OutOfMemoryError:Java heap space
heap 是Java内存中的堆区,主要用来存放对象,当对象太多超出了空间大小,GC又来不及释放的时候,就会发生溢出错误。
Java中对象的创建是可控的,但是对象的回收是由GC自动的,一般来说,当已存在对象没有引用(即不可达)的时候,GC就会定时的来回收对象,释放空间。但是因为程序的设计问题,导致对象可达但是又没有用(即前文提到的内存泄露),当这种情况越来越多的时候,问题就来了。
针对这个问题,我们需要做一下两点:
1、检查程序,减少大量重复创建对象的死循环,减少内存泄露。
2、增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。
3、StackOverFlowError
stack是Java内存中的栈空间,主要用来存放方法中的变量,参数等临时性的数据的,发生溢出一般是因为分配空间太小,或是执行的方法递归层数太多创建了占用了太多栈帧导致溢出。
针对这个问题,除了修改配置参数-Xss参数增加线程栈大小之外,优化程序是尤其重要。
十二、==和equals的区别?
== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。
十三、hashCode方法的作用?
其主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。
这样解决了向含有大量数据的集合中添加元素时,大量频繁的操作equals方法的问题。
十三、NIO是什么?适用于何种场景?
java.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。java.nio 包定义了缓冲区类,这些类用于所有 NIO API。java.nio.charset包中定义了字符集API,java.nio.channels包中定义了信道和选择器 API。每个子包都具有自己的服务提供程序接口(SPI) 子包,SPI 子包的内容可用于扩展平台的默认实现或构造替代实现。
适用的场景:如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,这时候用NIO处理数据可能是个很好的选择。
十四、HashMap实现原理,如何保证HashMap的线程安全?
简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。HashMap有4个构造器,其他构造器如果用户没有传入initialCapacity 和loadFactor这两个参数,会使用默认值
initialCapacity默认为16,loadFactory默认为0.75。
(一)java.util.Hashtable类
(二)使用 java.util.Collections.synchronizedMap(Map
(三) 使用 java.util.concurrent.ConcurrentHashMap 类。这是 HashMap 的线程安全版,同 Hashtable 相比,ConcurrentHashMap 不仅保证了访问的线程安全性,而且在效率上有较大的提高。
十五、JVM内存结构,为什么需要GC?
java语言并不要求JVM有gc,也没有规定gc如何工作。不过常用的JVM都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。理解了应用程序的工作负荷和JVM支持的垃圾收集算法,便可以进行优化配置垃圾收集器。垃圾收集的目的在于清除不再使用的对象,gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是时候可以收集,引用计数和对象引用遍历是两种常用的方法。
十六、NIO模型,select/epoll的区别,多路复用的原理
recvfrom从用户空间到内核空间的时候,如果该缓冲区没有数据的话,就直接返回一个EWOULDBOCK错误,一般都对非阻塞I/O模型进行轮询检查这个状态,看内核空间是不是有数据到来,有数据到来则从内核空间复制数据到用户空间
1.select的句柄数目受限,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE 1024 表示select最多同时监听1024个fd。而epoll没有,它的限制是最大的打开文件句柄数目。
2.epoll的最大好处是不会随着FD的数目增长而降低效率,在selec中采用轮询处理,其中的数据结构类似一个数组的数据结构,而epoll 是维护一个队列,直接看队列是不是空就可以了。epoll只会对”活跃”的socket进行操作—-这是因为在内核实现中epoll是根据每个fd上面 的callback函数实现的。那么,只有”活跃”的socket才会主动的去调用 callback函数(把这个句柄加入队列),其他idle状态句柄则不会,在这点上,epoll实现了一个”伪”AIO。但是如果绝大部分的I/O都是 “活跃的”,每个I/O端口使用率很高的话,epoll效率不一定比select高(可能是要维护队列复杂)。
3.使用mmap加速内核与用户空间的消息传递。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。
多路复用的原理:
IO多路复用原理:
其实就是整个函数对外表现为阻塞式的,也就是我们调用这个函数,如果条件达不到一定
会被阻塞;但是其实内部并不是阻塞的,而是以一种非阻塞的方式工作的,内部能够实现
自动轮询,如果有任何一个IO设备达到条件即可返回到应用层。
十七、Java中一个字符占多少个字节,扩展再问int, long, double占多少字节
1. java中内码(运行内存)中的char使用UTF16的方式编码,一个char占用两个字节,但是某些字符需要两个char来表示。所以,一个字符会占用2个或4个字节。
2. java中外码中char使用UTF8的方式编码,一个字符占用1~6个字节。
3. UTF16编码中,英文字符占两个字节;绝大多数汉字(尤其是常用汉字)占用两个字节,个别汉字(在后期加入unicode编码的汉字,一般是极少用到的生僻字)占用四个字节。
4. UTF8编码中,英文字符占用一个字节;绝大多数汉字占用三个字节,个别汉字占用四个字节。
int占4个字节,long占8个,double占8个。
十八、创建一个类的实例都有哪些办法?
1、 关键字 new。工厂模式是对这种方式的包装;
2、 类实现克隆接口,克隆一个实例。原型模式是一个应用实例;
3、 用该类的加载器,newinstance。java的反射,反射使用实例:Spring的依赖注入、切面编程中动态代理
4、 sun.misc.Unsafe类,allocateInstance方法创建一个实例。(Java官方也不建议直接使用的Unsafe类,据说Oracle正在计划从Java 9中去掉Unsafe类)
5、 实现序列化接口的类,通过IO流反序列化读取一个类,获得实例
十九、final/finally/finalize的区别?
(1)final为关键字;
(2)finalize()为方法;
(3)finally为为区块标志,用于try语句中;
(1)final为用于标识常量的关键字,final标识的关键字存储在常量池中(在这里final常量的具体用法将在下面进行介绍);
(2)finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);
(3)finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行;
二十、Session/Cookie的区别?
存储数据量方面:session 能够存储任意的 java 对象,cookie 只能存储 String 类型的对象
一个在客户端一个在服务端。因Cookie在客户端所以可以编辑伪造,不是十分安全。
Session过多时会消耗服务器资源,大型网站会有专门Session服务器,Cookie存在客户端没问题。
域的支持范围不一样,比方说a.com的Cookie在a.com下都能用,而www.a.com的Session在api.a.com下都不能用,解决这个问题的办法是JSONP或者跨域资源共享。
二十一、String/StringBuffer/StringBuilder的区别,扩展再问他们的实现?
String:不可变字符串;
StringBuffer:可变字符串、效率低、线程安全;
StringBuilder:可变字符序列、效率高、线程不安全;
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder
StringBuffer类继承自AbstractStringBuilder抽象类,实现Serializable序列化接口和CharSequence接口。
String类的一个最大特性是不可修改性,而导致其不可修改的原因是在String内部定义了一个常量数组,因此每次对字符串的操作实际上都会另外分配分配一个新的常量数组空间(这片空间位于jvm的静态方法区)
StringBuilder继承自AbstractStringBuilder,也就是说,new StringBuilder()这句代码,内部会创建一个长度为16的字符数组,count的默认值为0。
二十二、Servlet的生命周期?
1:加载Servlet
web容器负责加载Servlet,当web容器启动时或者是在第一次使用这个Servlet时,容器会负责创建Servlet实例,但是用户必须通过部署描述符(web.xml)指定Servlet的位置,也就是Servlet所在的类名称,成功加载后,web容器会通过反射的方式对Servlet进行实例化。
2:初始化
当一个Servlet初始化后,容器将调用init()方法初始化这个对象,初始化的目的是为了让Servlet在处理客户端请求前完成一些初始化的工作,如建立数据库连接,读取资源文件信息等,如果初始化失败,则次Servlet将被直接卸载。
3:进入服务
当有请求提交时,Servlet将调用service()方法进行处理,常用的是service根据请求类型调用doGet()或者doPost()方法进行处理;在service()方法中,Servlet可以通过ServletRequest接受客户的请求,也可以利用ServletResponse设置响应信息。
4:销毁
当web容器关闭或者检测到一个Servlet要从容器中被删除时,会自动调用destroy()方法,以便让该实例释放掉所占用的资源。
5:卸载
当一个Servlet调用完destroy()方法后,次实例将等待被垃圾收集器所回收,如果需要再次使用此Servlet时,会重新调用init()方法初始化。
二十三、如何用Java分配一段连续的1G的内存空间?需要注意些什么?
ByteBuffer.allocateDirect(102410241024); 栈中的内存是操作系统自动分配的,可以理解成为时连续的,对中的内存分配是因为我们malloc空间的时候,申请的空间的大小不一样造成了碎片。我们使用malloc的时候系统内部有一个空闲内存映射表,系统会自动查找空闲内存中的第一个合适大小的空间分配
二十四、Java有自己的内存回收机制,但为什么还存在内存泄露的问题呢?
Java中对内存对象的访问,使用的是引用的方式。在 Java 代码中我们维护一个内存对象的引用变量,通过这个引用变量的值,我们可以访问到对应的内存地址中的内存对象空间。在 Java 程序中,这个引用变量本身既可以存放堆内存中,又可以放在代码栈的内存中(与基本数据类型相同)。 GC 线程会从代码栈中的引用变量开始跟踪,从而判定哪些内存是正在使用的。如果 GC 线程通过这种方式,无法跟踪到某一块堆内存,那么 GC 就认为这块内存将不再使用了(因为代码中已经无法访问这块内存了)。
通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后,GC 就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被 GC 回收,哪怕是 Java 虚拟机抛出 OutOfMemoryError 。
内存泄漏的原因是:内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)
二十五、什么是java序列化,如何实现java序列化?(写一个实例)?
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
序列化是为了解决在对对象流进行读写操作时所引发的问题。序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,
然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。只有实现了Serializable和Externalizable接口的类的对象才能被序列化。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回
二十六、String s = new String(“abc”);创建了几个 String Object?
两个对于String s=new String(“abc”),这句话我们来大致分析一下步骤:
1.在虚拟机栈中为String类型的s分配内存
2.在堆中为分配一块内存用来保存”abc”
3.将堆中指向”abc”的地址赋值给s
二十七、JVM堆的基本结构。
java堆是和应用程序关系最为密切的内存空间,几乎所有的对象都存放在堆上。并且java堆是完全自动化管理的,通过垃圾回收机制,垃圾对象会被自动清理,而不需要显示的释放。
根据java回收机制的不同,java堆有可能拥有不同的结构。最为常见的一种构成是将整个java堆分为新生代和老年代。其中新生代存放新生对象或者年龄不大的对象,老年代则存放老年对象。新生代有可能分为eden区、s0区、s1区,s0区和s1区也被称为from和to区,他们是两块大小相同、可以互换角色的内存空间。
二十八、JVM的垃圾算法有哪几种?CMS垃圾回收的基本流程?
一共有七种:
1、Serial
2、ParNew
3、Parallel Scavenge
4、CMS
5、Serial Old(MSC)
6、Parrallel Old
7、G1
CMS如何执行?
总体来说CMS的执行过程可以分为以下几个阶段:
1 初始标记(STW)
2 并发标记
3 并发预清理
4 重标记(STW)
5 并发清理
6 重置
CMS(Concurrent Mark Sweep年老代)
· 年老代收集器,可以和Serial、ParNew组合使用
· 采用 ”标记-清除“算法,可以通过设置参数在垃圾回收时进行内存碎片的整理1、UserCMSCompactAtFullCollection:默认开启,FullGC时进行内存碎片整理,整理时用户进程需停止,即发生Stop The World2、CMSFullGCsBeforeCompaction:设置执行多少次不压缩的Full GC后,执行一个带压缩的(默认为0,表示每次进入Full GC时都进行碎片整理)
· CMS是并发算法,表示垃圾回收和用户进行同时进行,但是不是所有阶段都同时进行,在初始标记、重新标记阶段还是需要Stop the World。CMS垃圾回收分这四个阶段1、初始标记(CMS Initial mark) Stop the World 仅仅标记一下GC Roots能直接关联到的对象,速度快2、并发标记(CMS concurrent mark) 进行GC Roots Tracing,时间长,不发生用户进程停顿3、重新标记(CMS remark) Stop the World 修正并发标记期间因用户程序继续运行导致标记变动的那一部分对象的标记记录,停顿时间较长,但远比并发标记时间短4、并发清除(CMS concurrent sweep) 清除的同时用户进程会导致新的垃圾,时间长,不发生用户进程停顿
二十九、JVM有哪些常用启动参数可以调整,描述几个?
各主要JVM启动参数的作用如下:
-Xms:设置jvm内存的初始大小
-Xmx:设置jvm内存的最大值
-Xmn:设置新域的大小(这个似乎只对jdk1.4来说是有效的,后来就废弃了)
-Xss:设置每个线程的堆栈大小(也就是说,在相同物理内存下,减小这个值能生成更多的线程)
-XX:NewRatio:设置新域与旧域之比,如-XX:NewRatio=4就表示新域与旧域之比为1:4
-XX:NewSize:设置新域的初始值
-XX:MaxNewSize:设置新域的最大值
-XX:MaxPermSize:设置永久域的最大值
-XX:SurvivorRatio=n:设置新域中Eden区与两个Survivor区的比值。(Eden区主要是用来存放新生的对象,而两个Survivor区则用来存放每次垃圾回收后存活下来的对象)
三十、如何查看JVM的内存使用情况
Runtime run = Runtime.getRuntime();
long max = run.maxMemory()/(10241024);
long total = run.totalMemory()/(10241024);
long free = run.freeMemory()/(10241024);
long usable = max - total + free;
System.out.println(“最大内存 = “ + max);
System.out.println(“已分配内存 = “ + total);
System.out.println(“已分配内存中的剩余空间 = “ + free);
System.out.println(“最大可用内存 = “ + usable);
三十一、Java程序是否会内存溢出,内存泄露情况发生?举几个例子。
一般来说内存泄漏有两种情况。一种情况如在C/C++语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError
如果虚拟机在扩展栈时无法申请到足够的内存空间,将抛出OutOfMemoryError。
实验表明:在单线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError。
三十二、你常用的JVM配置和调优参数都有哪些?分别什么作用?
Trace跟踪参数(跟踪GC、类、变量的内存变化情况)
打开GC跟踪日志(每次执行GC的信息都能打印,获得执行时间,空间大小):
-verbose:gc 或 -XX:+printGC 或 -XX:+printGCDetails
类加载监控:(监控类加载的顺序)
-XX:+TraceClassLoading
二、堆的分频参数
-Xmx10M 指定最大堆,JVM最多能够使用的堆空间 (超过该空间引发OOM)
-Xms5M 指定最小堆,JVM至少会有的堆空间(尽可能维持在最小堆)
-Xmn 11M(new) 设置新生代大小
三十三、JVM的内存结构?
根据《Java虚拟机规范》的规定,运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)
三十四、常用的GC策略,什么时候会触发YGC,什么时候触发FGC
YGC的时机:edn空间不足
FGC的时机:1.old空间不足;2.perm空间不足;3.显示调用System.gc() ,包括RMI等的定时触发;4.YGC时的悲观策略;5.dump live的内存信息时(jmap –dump:live)。
YGC :对新生代堆进行GC。频率比较高,因为大部分对象的存活寿命较短,在新生代里被回收。性能耗费较小。
FGC :全堆范围的GC。默认堆空间使用到达80%(可调整)的时候会触发FGC。以我们生产环境为例,一般比较少会触发FGC,有时10天或一周左右会有一次。
三十五、如何创建线程?如何保证线程安全?
1) 继承Thread类;2)实现Runnable接口。
2) 一般说来,确保线程安全的方法有这几个:竞争与原子操作、同步与锁、可重入、过度优化。
三十六、如何实现一个线程安全的数据结构
1、使用synchronized来进行约束:
synchronized(obj){
value = map.get(key);
}
2、使用JDK1.5版本所提供的lock机制,java.util.concurrent.locks.Lock:
lock.lock();
value = map.get(key);
lock.unlock();
3、使用JDK提供的读写锁java.util.concurrent.locks.ReadWriteLock:
rwlock.readLock().lock();
value= map.get(key);
rwlock.readLock().unlock();
这样两个读操作可以同时进行,理论上效率比lock要高;
4、使用JDK1.5提供的java.util.concurrent.ConcurrentHashMap,该类将Map的存储空间分为若干块,每块拥有自己的锁,减少了多个线程争夺同一个锁的情况:
value=map.get(key);其中,同步机制已经在get方法中实现。
三十七、如何避免死锁
1、避免给一个锁嵌套上锁
在持有一个锁的时候,不要再给这个锁上锁。如果使用多个锁,使用std::lock。
2、在持有锁时,不要调用别人提供的函数
因为你不清楚别人的代码怎么实现的,不知道它是不是在使用锁。
3、给多个锁上锁时,固定顺序。
如果在给多个所上锁,并且无法使用std::lock,最好的做法就是在每一个线程中,都按照同样的顺序。
4、分层次来使用锁
把程序分成几个层次。区分每个层次中使用的锁,当一个线程已经持有更低层次的锁时,不允许使用高层次的锁。可以在程序运行时给不同的锁加上层次号,记录每个线程持有的锁。
三十八、Volatile关键字的作用?
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
第一:使用volatile关键字会强制将修改的值立即写入主存;
第二:使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);
第三:由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。
那么在线程2修改stop值时(当然这里包括2个操作,修改线程2工作内存中的值,然后将修改后的值写入内存),会使得线程1的工作内存中缓存变量stop的缓存行无效,然后线程1读取时,发现自己的缓存行无效,它会等待缓存行对应的主存地址被更新之后,然后去对应的主存读取最新的值。
三十九、HashMap在多线程环境下使用需要注意什么?为什么?
在多线程环境下,需要使用ConcurrentHashMap,因为HashMap是线程不安全的,如果多线程操作,会造成不可预期的结果。
四十、Java程序中启动一个线程是用run()还是start()?
start()是重新开启一个线程运行。 如果是run(),那就相当于在当前线程运行,并不会新创建一个线程运行。
四十一、什么是守护线程?有什么用?
守护线程是运行在后端的线程,当系统停止运行,守护线程也就停止了。
进行内存回收,垃圾清理等工作
四十二、线程和进程的差别是什么?
线程是进程的实现,进程是线程的体现。进程是独立的,线程运行在进程内。进程内可以有多个线程同时运行着。
线程是在代码层面上,进程是在服务器CPU内存上。
四十三、Java里面的Threadlocal是怎样实现的?
ThreadLocal的实现:维护一个Map,以Thread作为键,以变量作为值,每一次要使用变量的时候,就以当前的Thread作为键去取值,如果没有,就初始化一个值返回,如果有,就直接返回。
四十四、ConcurrentHashMap的实现原理是?
对大数组的每个值进行分离加锁,实现了锁分离
四十五、sleep和wait区别
sleep需要休眠时间,到点自然醒。会释放执行权,没有释放锁。设计在了Thread类上,是一个静态方法。
wait可以不指定等待时间,也可以指定等待时间。如果没有指定等待时间需要手动唤醒。wait必须结合锁使用。释放执行权,释放锁。设计在了Object上,是一个成员方法。
sleep是静态方法
四十六、notify和notifyAll区别
通知处在等待该对象的线程
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify是通知其中一个线程,而不会通知别的线程。
四十七、两个线程如何串行执行
同步的方法有很多中,最直接的就是”临界区”:依次只能由一个线程来执行的一段代码,另一个线程在第一个线程处理完之前是不会被执行的.
在使用临界区之前必须要初始化:InitializeCriticalSection
网上这样的成熟代码很多,你搜索一下吧.
其实你这样的情况使用同步基本上是不搭界的,你必须建立一个TStringList变量,一旦创建表时,该变量被赋值为这些表名的串SLA;执行SQL语句的线程的SQL语句应该来自于队列,该队列至少由相关的表名和SQL语句组成,执行的前提就是,该相关的表名中不能包含SLA的内容
四十八、可以运行时kill掉一个线程吗?
不能。Kill一个正在运行的进程,他的子进程有可能还在运行,会不停的重启。
四十九、什么是条件锁、读写锁、自旋锁、可重入锁?
自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行。若线程依然不能获得锁,才会被挂起。
读写锁、Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区分读写,称这种锁为普通锁。为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。
可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁
ReentrantLock类有一个方法newCondition用来生成这个锁对象的一个条件(ConditionObject)对象,它实现了Condition接口。Condition提供了线程通讯的一套机制await和signal等线程间进行通讯的方法。。
五十、线程池ThreadPoolExecutor的实现原理?
1. 如果当前运行的线程少于corePoolSize,则会创建新的线程来执行新的任务;
2. 如果运行的线程个数等于或者大于corePoolSize,则会将提交的任务存放到阻塞队列workQueue中;
3. 如果当前workQueue队列已满的话,则会创建新的线程来执行任务;
4. 如果线程个数已经超过了maximumPoolSize,则会使用饱和策略RejectedExecutionHandler来进行处理。
需要注意的是,线程池的设计思想就是使用了核心线程池corePoolSize,阻塞队列workQueue和线程池maximumPoolSize,这样的缓存策略来处理任务,实际上这样的设计思想在需要框架中都会使用
五十二、使用两种命令创建一个文件?
Cat touch
五十三、硬链接和软链接的区别?
软链接(A和B都是文件名),A的目录项中的inode节点号与B的目录项中的inode节点号不相同,A和B指向的是两个不同的inode,继而指向两块不同的数据块。但是A的数据块中存放的只是B的路径名(可以根据这个找到B的目录项)。A和B之间是“主从”关系,如果B被删除了,A仍然存在(因为两个是不同的文件),但指向的是一个无效的链接。
2.使用限制上:
硬链接:a.不能对目录创建硬链接,原因有几种,最重要的是:文件系统不能存在链接环(目录创建时的”..”除外,这个系统可以识别出来),存在环的后果会导致例如文件遍历等操作的混乱(du,pwd等命令的运作原理就是基于文件硬链接,顺便一提,ls -l结果的第二列也是文件的硬链接数,即inode节点的链接数)
b:不能对不同的文件系统创建硬链接,即两个文件名要在相同的文件系统下。
c:不能对不存在的文件创建硬链接,由原理即可知原因。
软链接:a.可以对目录创建软链接,遍历操作会忽略目录的软链接。
b:可以跨文件系统
c:可以对不存在的文件创建软链接,因为放的只是一个字符串,至于这个字符串是不是对于一个实际的文件,就是另外一回事了
3.命令
硬:ln 源文件 链接名
软:ln -s 源文件 链接名
*五十四、Linux常用命令有哪些?
arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS / DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT /dev/sda 在磁盘上执行测试性读取操作 cat /proc/cpuinfo 显示CPU info的信息 cat /proc/interrupts 显示中断 cat /proc/meminfo 校验内存使用 cat /proc/swaps 显示哪些swap被使用 cat /proc/version 显示内核的版本 cat /proc/net/dev 显示网络适配器及统计 cat /proc/mounts 显示已加载的文件系统 lspci -tv 罗列 PCI 设备 lsusb -tv 显示 USB 设备 date 显示系统日期 cal 2007 显示2007年的日历表 date 041217002007.00 设置日期和时间 - 月日时分年.秒 clock -w 将时间修改保存到 BIOS
关机 (系统的关机、重启以及登出 ) shutdown -h now 关闭系统(1) init 0 关闭系统(2) telinit 0 关闭系统(3) shutdown -h hours:minutes & 按预定时间关闭系统 shutdown -c 取消按预定时间关闭系统 shutdown -r now 重启(1) reboot 重启(2) logout 注销
文件和目录 cd /home 进入 ‘/ home’ 目录’ cd .. 返回上一级目录 cd ../.. 返回上两级目录 cd 进入个人的主目录 cd ~user1 进入个人的主目录 cd - 返回上次所在的目录 pwd 显示工作路径 ls 查看目录中的文件 ls -F 查看目录中的文件 ls -l 显示文件和目录的详细资料 ls -a 显示隐藏文件 ls [0-9] 显示包含数字的文件名和目录名 tree 显示文件和目录由根目录开始的树形结构(1) lstree 显示文件和目录由根目录开始的树形结构(2) mkdir dir1 创建一个叫做 ‘dir1’ 的目录’ mkdir dir1 dir2 同时创建两个目录 mkdir -p /tmp/dir1/dir2 创建一个目录树 rm -f file1 删除一个叫做 ‘file1’ 的文件’ rmdir dir1 删除一个叫做 ‘dir1’ 的目录’ rm -rf dir1 删除一个叫做 ‘dir1’ 的目录并同时删除其内容 rm -rf dir1 dir2 同时删除两个目录及它们的内容 mv dir1 new_dir 重命名/移动 一个目录 cp file1 file2 复制一个文件 cp dir/ . 复制一个目录下的所有文件到当前工作目录 cp -a /tmp/dir1 . 复制一个目录到当前工作目录 cp -a dir1 dir2 复制一个目录 ln -s file1 lnk1 创建一个指向文件或目录的软链接 ln file1 lnk1 创建一个指向文件或目录的物理链接 touch -t 0712250000 file1 修改一个文件或目录的时间戳 - (YYMMDDhhmm) file file1 outputs the mime type of the file as text
iconv -l 列出已知的编码 iconv -f fromEncoding -t toEncoding inputFile > outputFile creates a new from the given input file by assuming it is encoded in fromEncoding and converting it to toEncoding.
find . -maxdepth 1 -name .jpg -print -exec convert “{}” -resize 80x60 “thumbs/{}” \; batch resize files in the current directory and send them to a thumbnails directory (requires convert from Imagemagick)
五十五、怎么看一个Java线程的资源耗用?
,java程序一般都很耗资源,动辄都是好几G的内存,甚至几十G。对外,java进程是一个整体,而且java是共享内存模型,所以很难分析单个线程,但是我们还是可以分析java线程消耗cpu的情况
五十六、Load过高的可能性有哪些?
衡量CPU 系统负载的指标是load,load 就是对计算机系统能够承担的多少负载的度量,简单的说是进程队列的长度。请求大于当前的处理能力,会出现等待,引起load升高。
五十七、/etc/hosts文件什么做用?
Linux 的/etc/hosts是配置ip地址和其对应主机名的文件,这里可以记录本机的或其他主机的ip及其对应主机名。不同的linux版本,这个配置文件也可能不同。比如Debian的对应文件是/etc/hostname。
五十八、如何快速的将一个文本中所有“abc”替换为“xyz”?
cat file | tr “abc” “xyz” > new_file
五十九、如何在log文件中搜索找出error的日志?
1. 查找异常日志中业务代码包关键字,导出到一个文件中,例如这里搜索包含业务代码的异常:”at com.zw”
grep “at com.zw.” error.log > business.log
2. 祛除重复的行:
sort -k2n business.log | uniq > uniq.out
3. 根据uniq.out中的每一行去error.log文件中查找完整的异常;
六十、发现磁盘空间不够,如何快速找出占用空间最大的文件?
我们可以重复执行find命令来查找大文件,例如: find / -type f -size +5G
找到大于5G的文件。 find / -type f -size +1G
找到大于1G的文件
六十一、Java服务端问题排查(OOM,CPU高,Load高,类冲突)
常用jvm命令:
#虚拟机进程状况
jps [ options ] [ hostid ]
#虚拟机统计信息监视工具
jstat
#java配置信息工具。(pid为进程号)
jinfo [ option ] pid
#java 内存映像工具 (vmid一般是通过jps查到)
jmap [ option ] vmid
#虚拟机堆转储快照分析工具
jhat
#java堆栈跟踪工具 (vmid一般是通过jps查到)
jstack [ option ] vmid
六十二、Java常用问题排查工具及用法(top, iostat, vmstat, sar, tcpdump, jvisualvm, jmap, jconsole)
1、jstack
jstack可以告诉你当前所有JVM线程正在做什么,包括用户线程和虚拟机线程,你可以用它来查看线程栈,并且结合Lock信息来检测是否发生了死锁和死锁的线程。
没事儿jstack一下,知道你的小伙伴正在做什么。
另外在用top -H看到占用CPU非常高的pid时,可以转换成16进制后在jstack dump出来的文件中搜索,看看到底是什么线程占用了CPU。
2、jstat
stat,顾名思义就是提供一些统计信息,它可以告诉你当前的GC情况,包括GC次数、时间,具体的GC还可以结合gc.log文件去分析。
一般来说,我们用jstat去查看GC情况,判断是否存在YGC或FGC频繁的情况,再去看gc.log和jamp dump内存,MAT分析来定位问题(后面会有一个case针对这种场景)。
常用的用法是jstat -gcutil pid time_interval
3、jmap
排查GC问题必然会用到的工具,jmap可以告诉你当前JVM内存堆中的对象分布及其关系,当你dump堆之后可以用MAT分析,看看有哪些大对象,或者哪些类的实例特别多。
常用用法:
强制FGC:-histo:live
dump堆:-dump:[live],format=b,file=dump.bin
查看各代内存占用情况:-heap
然后我们来介绍一些开源的工具,来增强JVM工具本身的作用。
4、top
这个是 linux 自带的命令,查看系统资源消耗情况,可以看看CPU、内存、SWAP、I/O的消耗情况,需要特别注意的有几个值:
ni,这个值如果特别高说明线程上下文切换开销较大,看看是不是开了太多的线程导致的
res,这个代表了进程实际占用的内存
swap,内存不足就会占用swap空间,这个时候一般应用的性能会急剧下降,需要特别关注
六十三、Thread dump文件如何分析(Runnable,锁,代码栈,操作系统线程ID关联)
新建状态(New)
用new语句创建的线程处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存。
就绪状态(Runnable)
当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态,Java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。
运行状态(Running)
处于这个状态的线程占用CPU,执行程序代码。只有处于就绪状态的线程才有机会转到运行状态。
阻塞状态(Blocked)
阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU。直到线程重新进入就绪状态,它才有机会转到运行状态。
阻塞状态可分为以下3种:
① 位于对象等待池中的阻塞状态(Blocked in object’s wait pool):当线程处于运行状态时,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中,这涉及到“线程通信”的内容。
② 位于对象锁池中的阻塞状态(Blocked in object’s lock pool):当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中,这涉及到“线程同步”的内容。
③ 其他阻塞状态(Otherwise Blocked):当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求时,就会进入这个状态。
死亡状态(Dead)
当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。
六十四、如何查看Java应用的线程信息?
以下是针对tomcat上的应用的. 其他的java程序, 只要你能触发他的thread dump并且拿到结果, 也是一样.
1. ps -ef | grep java找到你的java程序的进程id, 定位 pid
2. top -Hp $pidshift+t 查看耗cpu时间最多的几个线程, 记录下线程的id
3. 把上诉线程ID转换成16进制小写 比如 : 0x12ef
4. kill -3 $pid 触发tomcat的thread dump
5. 找到tomcat的catalin.out 日志, 把 上面几个线程对应的代码段拿出来.DONE.
六十五、描述一下Hibernate的三个状态?
hibernate三种状态
Transient(临时状态):new出来的对象;它没有持久化,不存在于Session中——此状态中的对象为临时对象。
Persistent(持久化状态):已经持久化,存在于Session缓存中;如经hibernate语句save()保存的对象——此状态对象为持久对象。
Detached(游离状态):持久化对象脱离了Session后的对象。如Session缓存被清空后的对象。已经持久化,单不存在于Session中——此状态中的对象为游离对象。
六十六、Spring中Bean的生命周期。
实例化bean对象(通过构造方法或者工厂方法)
设置对象属性(setter等)(依赖注入)
如果Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。(和下面的一条均属于检查Aware接口)
如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身
将Bean实例传递给Bean的前置处理器的postProcessBeforeInitialization(Object bean, String beanname)方法
调用Bean的初始化方法
将Bean实例传递给Bean的后置处理器的postProcessAfterInitialization(Object bean, String beanname)方法
使用Bean
容器关闭之前,调用Bean的销毁方法
六十七、SpringMVC或Struts处理请求的流程。
SpringMVC的执行流程:
1:客户端发起请求到DispatcherServlet(前端控制器)
2:前端控制器请求HandlerMapping(处理器映射器)查找 Handler
根据xml配置、注解进行查找
3:HandlerMapping(处理器映射器)向DispatcherServlet(前端控制器)返回Handler
4:DispatcherServlet(前端控制器)调用HandlerAdapter(处理器适配器)去执行Handler
5:HandlerAdapter(处理器适配器)去执行Handler
6:Handler执行完成给HandlerAdapter(处理器适配器)返回ModelAndView
7:HandlerAdapter(处理器适配器)向前端控制器返回ModelAndView
ModelAndView是springmvc框架的一个底层对象,包括 Model和view
8:前端控制器请求ResolverView(视图解析器)去进行视图解析
根据逻辑视图名解析成真正的视图(jsp)
9:ResolverView(视图解析器)向DispatcherServlet(前端控制器)返回View
10:DispatcherServlet(前端控制器)进行视图渲染
视图渲染将模型数据(在ModelAndView对象中)填充到request域
最后DispatcherServlet(前端控制器)向用户响应(response)结果
Struts2的执行流程:1、客户端向服务器端发送一个请求,经过一系列的过滤器(在Struts2.0版本的时候叫做FilterDispatcher,在Struts2.1以上的版本叫做StrutsPrepareAndExecuteFilter),过滤器(StrutsPrepareAndExecuteFilter)会解析Struts.xml文件,请求查找相应的Action,如果没有则会报错,如果有则会生成对应的代理对象,然后经过一系列的拦截器,直到调用Action类中的execute()方法,处理结果由Struts2的处理器到核心过滤器再到服务器,最后由服务器响应(Response)到客户端。
六十八、Spring AOP解决了什么问题?怎么实现的?
AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。
Spring AOP有两种实现方式:
· 基于接口的动态代理(Dynamic Proxy)
· 基于子类化的CGLIB代理
关于动态代理和CGLIB这两种方式的简要总结如下:
o JDK动态代理(Dynamic Proxy)
o基于标准JDK的动态代理功能
o只针对实现了接口的业务对象
o CGLIB
o通过动态地对目标对象进行子类化来实现AOP代理,上面截图中的SampleBeanEnhancerByCGLIB1767dd4b即为动态创建的一个子类
o需要指定@EnableAspectJAutoProxy(proxyTargetClass = true)来强制使用
o当业务对象没有实现任何接口的时候默认会选择CGLIB
六十九、Spring事务的传播属性是怎么回事?它会影响什么
1 事务的传播属性(Propagation)
1) REQUIRED ,这个是默认的属性
Support a current transaction, create a new one if none exists. 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。
如图所示:
2) MANDATORY
Support a current transaction, throw an exception if none exists.支持当前事务,如果当前没有事务,就抛出异常。
3) NEVER
Execute non-transactionally, throw an exception if a transaction exists. 以非事务方式执行,如果当前存在事务,则抛出异常。
4) NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one exists. 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
5) REQUIRES_NEW
Create a new transaction, suspend the current transaction if one exists. 新建事务,如果当前存在事务,把当前事务挂起。
如图所示:
6) SUPPORTS
Support a current transaction, execute non-transactionally if none exists. 支持当前事务,如果当前没有事务,就以非事务方式执行。
7) NESTED
Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else. 支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。
嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
8) PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别
它们非常 类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。
使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。
七十、Spring中BeanFactory和FactoryBean有什么区别?
BeanFactory
BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。
FactoryBean
以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean
七十一、Spring框架中IOC的原理是什么
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
(5). Spring实现AOP:JDK动态代理和CGLIB代理 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理;其核心的两个类是InvocationHandler和Proxy。 CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强;需要引入包asm.jar和cglib.jar。 使用AspectJ注入式切面和@AspectJ注解驱动的切面实际上底层也是通过动态代理实现的。
七十二、spring的依赖注入有哪几种方式
1. 接口注入 2.set注入 3.构造注入
七十三、用Spring如何实现一个切面?
pring提供了4种实现AOP的方式:1.经典的基于代理的AOP2.@AspectJ注解驱动的切面3.纯POJO切面4.注入式AspectJ切面
七十四、Spring 如何实现数据库事务?
.第一步:把我们用的数据库连接池架上Spring,这里以C3P0 pool为例子
我把连接数据库的参参写成一个配置文件,读取出来的方式填入数据库连接池
第二步骤:写一个Spring事物管理器,记得要把DataSource放上
第三步:Spring的事物是通过注解实现的,某个类或接口上面有一个@Transactiond就会进行事物处理,所以我们需要让这个注解被认识
第四步:Spring的事物需要在Dao的实现类继承一个类,JdbcDaoSupport,同时他不需要注入写DataSource的Setter、Getter方法,原因是注入父类,继承的父类里面写了,调用方法也是Spring自己写的支持数据库的类里面的方法
七十五、Hibernate对一二级缓存的使用,Lazy-Load的理解
Hibenate中一级缓存,也叫做session的缓存,当调用session的save/saveOrUpdate/get/load/list/iterator方法的时候,都会把对象放入session的缓存中。
一级缓存可以在session范围内减少数据库的访问次数,只在session范围有效,session关闭,一级缓存失效。
session的缓存由hibernate维护, 用户不能操作缓存内容; 如果想操作缓存内容,必须通过hibernate提供的evit/clear方法操作。
特点:
只在当前session范围有效,作用时间短,效果不是特别明显!
在短时间内多次操作数据库,效果比较明显!
Hibernate提供了基于应用程序级别的缓存, 可以跨多个session,即不同的session都可以访问缓存数据。 这个缓存也叫二级缓存。
Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!如果用户想用二级缓存,只需要在hibernate.cfg.xml中配置即可; 不想用,直接移除,不影响代码。如果用户觉得hibernate提供的框架框架不好用,自己可以换其他的缓存框架或自己实现缓存框架。
当用到数据的时候才向数据库查询,这就是hibernate的懒加载特性。
lazy 值
true 使用懒加载
false 关闭懒加载
extra 在集合数据懒加载时候提升效率,在真正使用数据的时候才向数据库发送查询的sql,如果调用集合的size()/isEmpty()方法,只是统计,不真正查询数据!
对页面加载速度影响最大的就是图片,一张普通的图片可以达到几M的大小,而代码也许就只有几十KB。当页面图片很多时,页面的加载速度缓慢,几S钟内页面没有加载完成,也许会失去很多的用户。
所以,对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样子对于页面加载性能上会有很大的提升,也提高了用户体验。
原理
将页面中的img标签src指向一张小图片或者src为空,然后定义data-src(这个属性可以自定义命名,我才用data-src)属性指向真实的图片。src指向一张默认的图片,否则当src为空时也会向服务器发送一次请求。可以指向loading的地址。
当载入页面时,先把可视区域内的img标签的data-src属性值负给src,然后监听滚动事件,把用户即将看到的图片加载。这样便实现了懒加载。
懒加载的使用
1.引用< script undefinedEN-US”>http://a.tbcdn.cn/apps/baron/js/??lib/tmm/tmm.js,lib/lazyload/lazyload.js?20121015“>
2.对于要懒加载的图片进行如下属性设置。< img undefinedEN-US”>http://a.tbcdn.cn/mw/webapp/fav/img/grey.gif“ dataimg=”http://img03.taobaocdn.com/tps/i3/T1xl4_FlFaXXcLmU_5-305-317.png“ />其中src为未加载时的图片,dataimg为实际要加载的图片。
3.执行lazyload.init();
4.分tab的懒加载。判断tab把下面的图片有没加载过。根据loaded属性判断,还要对非当前tab所属的图片进行class lazy去掉。对已加载的loaded为true的图片,不加lazy属性
5.注意lazyload.init()的执行时机,如果在dom ready阶段执行,会下载所有图片,不能实现懒加载。要在winow.onload完成这个阶段去执行。
七十六、mybatis如何实现批量提交?
,在 Service 中直接注入了 SqlSessionFactory,通过下面方法获取了一个可以批量提交的 SqlSession:
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
后续通过 SqlSession 直接执行方法,或者获取的 Mapper 接口,都使用的批量提交方式。
七十七、MySQL InnoDB、Mysaim的特点?
innodb整体性能远高于myisam。同时,innodb的索引不仅需要缓存索引本身,也缓存数据,所以innodb需要更大的内存。如果你不知道一个表需要使用什么存储引擎,建议使用innodb。
mysql中主要的存储引擎是innodb、mysiam。下面介绍一下它们的区别:
innodb、mysiam
innodb:注重事物、行级锁、R/W比较少,频繁更新大字段
mysiam : 注重性能、表级锁、R/W > 100 :1且update相对较少
七十八、乐观锁和悲观锁的区别?
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于writecondition机制的其实都是提供的乐观锁。
七十九、数据库隔离级别是什么?有什么作用?
隔离级别就是对对事务并发控制的等级。[_ANSI](http://en.wikipedia.org/wiki/American_National_Standards_Institute)_/ [_ISO](http://en.wikipedia.org/wiki/International_Organization_for_Standardization)_ [_SQL](http://en.wikipedia.org/wiki/SQL)将其分为串行化(_SERIALIZABLE_)、可重复读(_REPEATABLE READ)、读已提交(_READ COMMITED)、读未提交(READ UNCOMMITED)四个等级
串行化(SERIALIZABLE):所有事务都一个接一个地串行执行,这样可以避免幻读(phantom reads)。对于基于锁来实现并发控制的数据库来说,串行化要求在执行范围查询(如选取年龄在10到30之间的用户)的时候,需要获取范围锁(range lock)。如果不是基于锁实现并发控制的数据库,则检查到有违反串行操作的事务时,需要滚回该事务。
可重复读(REPEATABLE READ):所有被Select获取的数据都不能被修改,这样就可以避免一个事务前后读取数据不一致的情况。但是却没有办法控制幻读,因为这个时候其他事务不能更改所选的数据,但是可以增加数据,因为前一个事务没有范围锁。
读已提交(READ COMMITED):被读取的数据可以被其他事务修改。这样就可能导致不可重复读。也就是说,事务的读取数据的时候获取读锁,但是读完之后立即释放(不需要等到事务结束),而写锁则是事务提交之后才释放。释放读锁之后,就可能被其他事物修改数据。该等级也是SQL Server默认的隔离等级。
读未提交(READ UNCOMMITED):这是最低的隔离等级,允许其他事务看到没有提交的数据。这种等级会导致脏读(Dirty Read)。
八十、MySQL主备同步的基本原理。
从库生成两个线程,一个I/O线程,一个SQL线程;
i/o线程去请求主库 的binlog,并将得到的binlog日志写到relay log(中继日志) 文件中;
主库会生成一个 log dump 线程,用来给从库 i/o线程传binlog;
SQL 线程,会读取relay log文件中的日志,并解析成具体操作,来实现主从的操作一致,而最终数据一致;
八十一、select * from table t where size > 10 group by size order by size的sql语句执行顺序?
查询语句用到的关键词主要包含6个,select-from-where-group by-having-order by,这是书写顺序。执行顺序是from-where-group by-having-select-order by
八十二、如何优化数据库性能(索引、分库分表、批量操作、分页算法、升级硬盘SSD、业务优化、主从部署)
1、索引,建立索引是数据库优化各种方案之中成本最低,见效最快的解决方案,一般来讲,数据库规模在几十万和几百万级别的时候见效最快,即便是有不太复杂的表关联,也能大幅度提高sql的运行效率,这个在我们以前的项目应用中,有非常深刻的体会,本来耗时50s的sql,在增加索引后可以提升到1-2s,而且不需要有代码改动,成本低廉,见效明显
2、分库分表分区
分库,可以按照业务分库,分流数据库并发压力,使数据库表更加有条理性,最起码更加好找吧,我们当时是把查询库和系统库(增删改比较频繁的表)分开了,这样如果有大查询,不影响系统库
分区的实现道理和分表一样,也是将相应规则的数据放在一起,唯一不同的是分区你只需要设定好分区规则,插入的数据会被自动插入到指定的区里,当然查询的时候也能很快查询到需要区,相当于是分表对外透明了,出现跨表数据库自动帮我们合并做了处理,使用起来比分表更加方便,但是分区也有自己的问题,每一个数据库表的并发访问是有上限的,也就是说,分表能够抗高并发,而分区不能,如何选择,要考虑实际情况
3、数据库引擎
也是偶尔听一个dba同事提到的,有一次我跟dba同事抱怨,我的数据库查询太慢,有没有好的优化方法,他一开始就问,数据量多大,有没有索引,使用的什么数据库引擎,这时我才意识到原来数据库引擎也算是一种优化方案
mysql比较常用的数据库引擎有两种,一种是innodb、一种是myisam
4、预处理
一般来说,实时数据(当天的数据)还是比较有限的,真正数据量比较大的是历史数据,基于大表历史数据的查询,如果再涉及一些大表关联,这种sql是非常难以优化的
5、mysql like查询
like “%str%” 不支持索引, “str%”号是支持索引的
因此,如果业务允许,可以使用前匹配的方法是数据库快速定位到数据,在结果集中再进行like匹配,如果这个结果集不是很大,是可以大幅提升运行效率的,这个需要对业务和程序有灵活的变通
6、读写分离
在数据库并发大的情况下,最好的做法就是进行横向扩展,增加机器,以提升抗并发能力,而且还兼有数据备份功能
八十三、SQL什么情况下不会使用索引(不包含,不等于,函数)
1、 查询谓词没有使用索引的主要边界,换句话说就是select ,可能会导致不走索引
2、 单键值的b树索引列上存在null值,导致COUNT()不能走索引
3、 索引列上有函数运算,导致不走索引
4、 、隐式转换导致不走索引。
5、 表的数据库小或者需要选择大部分数据,不走索引
6、 cbo优化器下统计信息不准确,导致不走索引
7、 表字段的属性导致不走索引,字符型的索引列会导致优化器认为需要扫描索引大部分数据且聚簇因子很大,最终导致弃用索引扫描而改用全表扫描方式,
八十四、一般在什么字段上建索引(过滤数据最多的字段)
外键字段
主键字段
在where子句中的字段
八十五、如何从一张表中查出name字段不包含“XYZ”的所有行?
not in where name LIKE “%XYZ%”
或者使用全文索引提高查询速度
八十六、MySQL,B+索引实现,行锁实现,SQL优化
B+的特性:
1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
2.不可能在非叶子结点命中;
3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
4.更适合文件索引系统;
InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。 InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!
Sql优化:
第一:一个表的索引不是越多越好,也没有一个具体的数字,根据以往的经验,一个表的索引最多不能超过6个,因为索引越多,对update和insert操作也会有性能的影响,涉及到索引的新建和重建操作。
第二:建立索引的方法论为:
1. 多数查询经常使用的列;
2. 很少进行修改操作的列;
3. 索引需要建立在数据差异化大的列上
八十七、Redis,RDB和AOF,如何做高可用、集群
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
八十八、如何解决高并发减库存问题
在业务复杂,数据量大,并发量大的情况下,库存扣减容易引发数据的不一致,常见的优化方案有两个:
· 调用“设置库存”接口,能够保证数据的幂等性
· 在实现“设置库存”接口时,需要加上原有库存的比较,才允许设置成功,能解决高并发下库存扣减的一致性问题
八十九、mysql存储引擎中索引的实现机制
MyISAM和InnoDB两个存储引擎的索引虽然都是使用的B+Tree数据结构,但是在具体实现上还是存在不小差别的。InnoDB支持聚簇索引,聚簇索引就是表,所以InnoDB不用像MyISAM那样需要独立的行存储。也就是说,InnoDB的数据文件本身就是索引文件。而MyISAM的数据文件和索引文件是分开存储的。可以通过MyISAM和InnoDB如何存放表的抽象图帮助快速理解。
九十、数据库事务的几种粒度
在sql server2000中锁是具有粒度的,即可以对不同的资源加锁。锁定在较小的粒度的资源(例如行)上可以增加系统的并发量但需要较大的系统开销,从而也会影响系统的性能,因为锁定的粒度较小则操作可能产生的锁的数量会增加;锁定在较大的粒度(例如表)就并发而言是相当昂贵的,因为锁定整个表限制了其它事务对表中任意部分进行访问,但要求的开销较低,因为需要维护的锁较少,所以在这里是一种互相制约的关系。
Sql server2000中锁定的粒度包括 行、页、扩展盘区、表、库等资源。
九十一、TCP建立连接的过程。
(1)第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。
(2)第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。
(3)第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。
九十二、TCP断开连接的过程
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
CP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。
九十三、浏览器发生302跳转背后的逻辑?
302状态码应用的典型场景是服务器页面路径的重新规划,
比如一个portal页面,换了新的域名,但是老的域名地址还有很多用户在使用,这样可以对老域名配置302跳转到新域名地址,保证服务的延续。
另外对于一些客户端预埋的Url链接,免不了老版本地址失效与更改,将老地址配置302跳转到新地址,这样就能够全面兼容所有客户端版本。
九十四、HTTP协议的交互流程。HTTP和HTTPS的差异,SSL的交互流程?
1. 客户端请求SSL连接,并将自己支持的加密规则发给网站。
2. 服务器端将自己的身份信息以证书形式发回给客户端。证书里面包含了网站地址,加密公钥,以及证书的颁发机构。
3. 获得证书后,客户要做以下工作:
1)验证证书合法性
2)如果证书受信任,客户端会生成一串随机数的密码,并用证书提供的公钥进行加密。
3)将加密好的随机数发给服务器。
4. 获得到客户端发的加密了的随机数之后,服务器用自己的私钥进行解密,得到这个随机数,把这个随机数作为对称加密的密钥。
5. 之后服务器与客户之间就可以用随机数对各自的信息进行加密,解密。
1. HTTPS协议需要申请证书。 2.HTTP是明文传输;HTTPS使用的是具有安全性的SSL加密传输协议 3.HTTP端口是80;HTTPS端口号是443 4.HTTP连接简单无状态;HTTPS由SSL+HTTP协议构件的可进行加密传输、身份验证的网络协议
九十五、Rest和Http什么关系? 大家都说Rest很轻量,你对Rest风格如何理解?
统一接口
这里其实是让http回归本质,http协议本身就自带method,而且还有表示各种不同状态的返回码等等。
当我们面向资源设计系统的时候,只要在资源层次对客户端和服务器端开放。这时候增删查改都有了统一接口,减少沟通交流成本。
架构扩展-十年不会过时的软件系统架构
REST本身不是架构,只是一种架构风格,理解它的时候要参考这个架构风格出现的环境所施加的约束条件。
REST的目的是“建立十年内不会过时的软件系统架构”,所以它具备三个特点:
状态无关 —— 确保系统的横向拓展能力
超文本驱动,Fielding的原话是”hypertext-driven” —— 确保系统的演化能力
对 resource 相关的模型建立统一的原语,例如:uri、http的method定义等 —— 确保系统能够接纳多样而又标准的客户端
从另外一个角度看,第一条保证服务端演化,第三条保证客户端演化,第二条保证应用本身的演化,这实在是一个极具抽象能力的方案。
九十六、TCP的滑动窗口协议有什么用?讲讲原理
滑动窗口基本原理1)对于TCP会话的发送方,任何时候在其发送缓存内的数据都可以分为4类,“已经发送并得到对端ACK的”,“已经发送但还未收到对端ACK
的”,“未发送但对端允许发送的”,“未发送且对端不允许发送”。“已经发送但还未收到对端ACK的”和“未发送但对端允许发送的”这两部分数据称之为发
送窗口。
当收到接收方新的ACK对于发送窗口中后续字节的确认是,窗口滑动,滑动原理如下图。
当收到ACK=36时窗口滑动。2)对于TCP的接收方,在某一时刻在它的接收缓存内存在3种。“已接收”,“未接收准备接收”,“未接收并未准备接收”(由于ACK直接由TCP协议栈回复,默认无应用延迟,不存在“已接收未回复ACK”)。其中“未接收准备接收”称之为接收窗口。
发送窗口与接收窗口关系TCP是双工的协议,会话的双方都可以同时接收、发送数据。TCP会话的双方都各自维护一个“发送窗口”和一个“接收窗口”。其中各自的“接收窗
口”大小取决于应用、系统、硬件的限制(TCP传输速率不能大于应用的数据处理速率)。各自的“发送窗口”则要求取决于对端通告的“接收窗口”,要求相
同。
九十七、HTTP协议都有哪些方法
HTTP请求的方法:
HTTP/1.1协议中共定义了八种方法(有时也叫“动作”),来表明Request-URL指定的资源不同的操作方式
1、OPTIONS
返回服务器针对特定资源所支持的HTTP请求方法,也可以利用向web服务器发送‘’的请求来测试服务器的功能性
2、HEAD
向服务器索与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以再不必传输整个响应内容的情况下,就可以获取包含在响应小消息头中的元信息。
3、GET
向特定的资源发出请求。它本质就是发送一个请求来取得服务器上的某一资源。资源通过一组HTTP头和呈现数据(如HTML文本,或者图片或者视频等)返回给客户端。GET请求中,永远不会包含呈现数据。
4、POST
向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 Loadrunner中对应POST请求函数:web_submit_data,web_submit_form
5、PUT
向指定资源位置上传其最新内容
6、DELETE
请求服务器删除Request-URL所标识的资源
7、TRACE
回显服务器收到的请求,主要用于测试或诊断
8、CONNECT
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
九十八、交换机和路由器的区别?
外形上
(2)工作层次不同:
最初的交换机工作在OSI开放式系统互联模型的数据链路层,也就是第二层,而路由器则工作在OSI模型的网络层,就是第三层。也就是由于这一点所以交换机的原理比较简单,一般都是采用硬件电路实现数据帧的转发,而路由器工作在网络层,肩负着网络互联的重任,要实现更加复杂的协议,具有更加智能的转发决策功能,一般都会在在路由器中跑操作系统,实现复杂的路由算法,更偏向于软件实现其功能。(3)数据的转发对象不同:
交换机是根据MAC地址转发数据帧,而路由器则是根据IP地址来转发IP数据报/分组。数据帧是在IP数据包/分组的基础上封装了帧头(源MAC和目的MAC等)和帧尾(CRC校验码)。而对于MAC地址和IP地址大家也许就搞不明白了,为何需要两个地址,实际上IP地址决定最终数据包要到达某一台主机,而MAC地址则是决定下一跳将要交互给哪一台设备(一般是路由器或主机)。而且,IP地址是软件实现的,可以描述主机所在的网络,MAC地址是硬件实现的,每一个网卡在出厂的时候都会将全世界唯一的MAC地址固化在网卡的ROM中,所以MAC地址是不能被修改的,但是IP地址是可以被网络管理人员配置修改的。
(4)”分工“不同
交换机主要是用于组建局域网,而路由器则是负责让主机连接外网。多台主机可以通过网线连接到交换机,这时就组建好了局域网,就可以将数据发送给局域网中的其他主机,如我们使用的飞秋、极域电子教室等局域网软件就是通过交换机把数据转发给其他主机的,当然像极域电子教室这样的广播软件是利用广播技术让所有的主机都收到数据的。然而,通过交换机组建的局域网是不能访问外网的(即是Internet),这时需要路由器来为我们”打开外面精彩世界的大门“,局域网的所有主机使用的都是私网的IP,所以必须通过路由器转化为公网的IP之后才能访问外网。
(5)冲突域和广播域
交换机分割冲突域,但是不分割广播域,而路由器分割广播域。由交换机连接的网段仍属于同一个广播域,广播数据包会在交换机连接的所有网段上传播,在这种情况下会导致广播风暴和安全漏洞问题。而连接在路由器上的网段会被分配不通的广播域,路由器不会转发广播数据。需要说明的是单播的数据包在局域网中会被交换机唯一地送往目标主机,其他主机不会接收到数据,这是区别于原始的集线器的,数据的到达时间由交换机的转发速率决定,交换机会转发广播数据给局域网中的所有主机。
最后需要说明的是:路由器一般有防火墙的功能,能够对一些网络数据包选择性过滤。现在的一些路由器都具备交换机的功能(如上图右),一些交换机具备路由器的功能,被称为3层交换机,广泛使用。相比较而言,路由器的功能较交换机要强大,但是速度也较慢,价格昂贵,三层交换机既有交换机的线性转发报文的能力,又有路由器的良好的路由功能因此得到广泛的使用。
九十九、Socket交互的基本流程?
第一步:用指定的端口号和服务器的ip建立一个EndPoint对象;
第二步:建立一个Socket对象;
第三步:用socket对象的Bind()方法绑定EndPoint;
第四步:用socket对象的Listen()方法开始监听;
第五步:接受到客户端的连接,用socket对象的Accept()方法创建新的socket对象用于和请求的客户端进行通信;
第六步:通信结束后一定记得关闭socket;
客户端:
第一步:用指定的端口号和服务器的ip建立一个EndPoint对象;
第二步:建立一个Socket对象;
第三步:用socket对象的Connect()方法以上面建立的EndPoint对象做为数,向服务器发出连接请求;
第四步:如果连接成功,就用socket对象的Send()方法向服务器发送信息;
第五步:用socket对象的Receive()方法接受服务器发来的信息 ;
第六步:通信结束后一定记得关闭socket;
*一百、webservice协议(wsdl/soap格式,与rest协议的区别)
REST 与SOAP的比较:
· 成熟度
SOAP目前成熟,不同平台,开发语言之间通过SOAP来交互的web service都能够较好的互通。REST相对不太成熟,由于没有类似于SOAP的权威性协议作为规范,REST实现的各种服务风格不一,通用性不强。
· 效率和易用性
SOAP使用门槛高(学习成本高,开发难度大),由于SOAP由于各种需求不断扩充其本身协议的内容,在大并发下性能有所下降。REST 目前大量的Web 2.0网站使用,高效以及简洁易用。这种高效一方面源于其面向资源接口设计以及操作抽象简化了开发者的不良设计,同时也最大限度的利用了Http最初的应用协议设计理念。REST 是一种轻量级的Web Service架构风格,其实现和操作明显比SOAP和XML-RPC更为简洁,可以完全通过HTTP协议实现,还可以利用缓存Cache来提高响应速度,性能、效率和易用性上都优于SOAP协议。
· 安全性
SOAP在安全方面是通过使用XML-Security和XML-Signature两个规范组成了WS-Security来实现安全控制的,当前已经得到了各个厂商的支持,.net ,php ,java 都已经对其有了很好的支持。REST没有任何规范对于安全方面作说明。因此在考虑安全性上,SOAP要高于REST。
总的来说,我认为REST对于资源型服务接口来说很合适,同时特别适合对于效率要求很高,但是对于安全要求不高的场景。而SOAP的成熟性可以给需要提供给多开发语言的,对于安全性要求较高的接口设计带来便利。
一百零一、NIO的好处,Netty线程模型,什么是零拷贝
Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)
Netty同时支持Reactor单线程模型 、Reactor多线程模型和Reactor主从多线程模型,用户可根据启动参数配置在这三种模型之间切换
服务端启动时,通常会创建两个NioEventLoopGroup实例,对应了两个独立的Reactor线程池
零拷贝是指计算机操作的过程中,CPU不需要为数据在内存之间的拷贝消耗资源。而它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而直接在内核空间(Kernel Space)中传输到网络的方式。 Zero Copy的模式中,避免了数据在用户空间和内存空间之间的拷贝,从而提高了系统的整体性能。Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都实现了零拷贝的功能,而在Netty中也通过在FileRegion中包装了NIO的FileChannel.transferTo()方法实现了零拷贝。
一百零二、列举一个常用的Redis客户端的并发模型。
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有2种解决方法:
1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。
2.服务器角度,利用setnx实现锁。如某客户端要获得一个名字list的锁,客户端使用下面的命令进行获取:
Setnx lock.list current time + lock timeout
如返回1,则该客户端获得锁,把lock. list的键值设置为时间值表示该键已被锁定,该客户端最后可以通过DEL lock.list来释放该锁。
如返回0,表明该锁已被其他客户端取得,等对方完成或等待锁超时。
第二种需要用到Redis的setnx命令,但是需要注意一些问题。
一百零三、HBase如何实现模糊查询?
现有的元数据是存放在HBase中的,rowkey为了防止热点现象,采取了id倒序的设计。绝大部分场景是用户直接访问rowkey获取对应的内容。现在要求加上搜索功能,原来的rowkey不能改,只能构建二级索引去指向元数据的rowkey,通过二次查找来获取元数据。这里二级索引rowkey设计规则可以是
id倒序 + 日期 + 标题
1
其中id倒序用来防止热点现象,日期和标题也作为rowkey的组成部分方便后续用RowFilter进行模糊查询,HBase对rowkey的操作要比对column的操作性能要好上很多。
分页功能可用PageFilter实现。
一百零四、列举一个常用的消息中间件,如果消息要保序如何实现?
中间件:tomcat
原理: tomat是一个servlet容器,来处理http请求。在平时的使用中我们都会再浏览器中输入http地址来访问服务资源,比如格式http://host[":"port][abs_path]。从浏览器到服务端的一次请求都遵循http协议,在网络上其实走仍然是tcp协议,即我们常使用的socket来处理客户端和服务器的交互。根据输入的http地址可以知道服务器的IP地址和端口,根据这两个参数就可以定位到服务器的唯一地址。tomcat根据http地址端口后面的资源路径就可以知道反馈什么样的资源给浏览器。下面给出了一个非常简单的代码模拟了tomcat的简单实现
一百零五、如何实现一个Hashtable?你的设计如何考虑Hash冲突?如何优化?
Hashtable继承于Dictionary类,实现了Map接口。Map是”key-value键值对”接口,Dictionary是声明了操作”键值对”函数接口的抽象类。
(02) Hashtable是通过”拉链法”实现的哈希表。它包括几个重要的成员变量:table, count, threshold, loadFactor, modCount。
table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的”key-value键值对”都是存储在Entry数组中的。
count是Hashtable的大小,它是Hashtable保存的键值对的数量。
threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值=”容量*加载因子”。
loadFactor就是加载因子。
modCount是用来实现fail-fast机制的
线性探测再散列
这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。
二次探测再散列
这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。
伪随机探测再散列
一百零六、分布式缓存,一致性hash
先构造一个长度为2^32的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 2^32-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为[0, 2^32-1]),接着在Hash环上顺时针查找距离这个Key值的Hash值最近的服务器节点,完成Key到服务器的映射查找。
一百零七、什么是布隆过滤器,其实现原理是? False positive指的是?
布隆过滤器 (Bloom Filter)是由Burton Howard Bloom于1970年提出,它是一种space efficient的概率型数据结构,用于判断一个元素是否在集合中。在垃圾邮件过滤的黑白名单方法、爬虫(Crawler)的网址判重模块中等等经常被用到。哈希表也能用于判断元素是否在集合中,但是布隆过滤器只需要哈希表的1/8或1/4的空间复杂度就能完成同样的问题。布隆过滤器可以插入元素,但不可以删除已有元素。其中的元素越多,false positive rate(误报率)越大,但是false negative (漏报)是不可能的。
一百零八、memcache与redis的区别
1、性能
都比较高,性能对我们来说应该都不是瓶颈
总体来讲,TPS方面redis和memcache差不多,要大于mongodb
2、操作的便利性
memcache数据结构单一
redis丰富一些,数据操作方面,redis更好一些,较少的网络IO次数
mongodb支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富
3、内存空间的大小和数据量的大小
redis在2.0版本后增加了自己的VM特性,突破物理内存的限制;可以对key value设置过期时间(类 似memcache)
memcache可以修改最大可用内存,采用LRU算法
mongoDB适合大数据量的存储,依赖操作系统VM做内存管理,吃内存也比较厉害,服务不要和别的服 务在一
4、可用性(单点问题)
对于单点问题,
redis,依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快 照,无增量复制,因性能和效率问题,
所以单点问题比较复杂;不支持自动sharding,需要依赖程序设定一致hash 机制。
一种替代方案是,不用redis本身的复制机制,采用自己做主动复制(多份存储),或者改成增量复 制的方式(需要自己实现),一致性问题和性能的权衡
Memcache本身没有数据冗余机制,也没必要;对于故障预防,采用依赖成熟的hash或者环状的算 法,解决单点故障引起的抖动问题。
mongoDB支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制。
5、可靠性(持久化)
对于数据持久化和数据恢复,
redis支持(快照、AOF):依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响
memcache不支持,通常用在做缓存,提升性能;
MongoDB从1.8版本开始采用binlog方式支持持久化的可靠性
6、数据一致性(事务支持)
Memcache 在并发场景下,用cas保证一致性
redis事务支持比较弱,只能保证事务中的每个操作连续执行
mongoDB不支持事务
7、数据分析
mongoDB内置了数据分析的功能(mapreduce),其他不支持
8、应用场景
redis:数据量较小的更性能操作和运算上
memcache:用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能(适合读多写少,对于 数据量比较大,可以采用sharding)
MongoDB:主要解决海量数据的访问效率问题
一百零九、zookeeper有什么功能,选举算法如何进行
功能:节点选举、统一配置文件、发布与订阅、提供分布式锁、集群管理。
选举算法:选举要有两个阶段,第一阶段,恢复数据阶段,当主节点宕机之后,各个从节点服务器先恢复数据,然后比较各自的事务id,如果事务id大的就当leader,如果相同进入第二阶段,比较各自的选举ID。根据的是服务器的启动顺序来决定的,过半选举。
一百一十、map/reduce过程,如何用map/reduce实现两个数据源的联合统计
在Map输入阶段获取输入路径,在Map输出阶段根据路径的不同加以区分,即在将关联的列作为Key,在 Value中加以区分是哪个数据源的数据,接着在Reduce的输入阶段,在reduce方法的入参会得到所有 Key相同的集合,这样便可对数据进行响应的组装,这样便可完成2个数据源的关联,多个数据源的关联 也类似。
一百一十一、你能举例几个常见的设计模式
1.单例模式(一共8种):饿汉式(2)、懒汉式(3)、枚举、双重检查、静态内部类
2.装饰者模式:Buffer:StringBuffer、StringBuilder
3.工厂模式:springIOC(Bean工厂)
4.代理模式:SpringAOP底层用的jdk动态代理跟cglib动态代理。
一百一十二、你在设计一个工厂的包的时候会遵循哪些原则?
1.开闭原则,具有很强的的扩展性、弹性和可维护性。扩展时只要添加一个ConcreteCreator, 而无须修改原有的ConcreteCreator,因此维护性也好。解决了简单工厂对修改开放的问题。
2.使用了依赖倒置原则,依赖抽象而不是具体,使用(客户)和实现(具体类)松耦合。
一百一十三、你能列举一个使用了Visitor/Decorator模式的开源项目/库吗?
Visitor模式应用场景:如果一个应用程序存在有需要以多种不同方式进行解释的数据结构,就可以使用Visitor模式。示例:
解决问题: 在Modern接口中增加ConfigureForUnix方法,ConfigureForWin方法或者其他平台的方法。
Visitor 模式如同一个矩阵,图1例子中,矩阵的一条轴是不同类型的调制解调器,另一条轴是不同类型的操作系统。该矩阵的每一个单元都被一项功能填充,该功能描绘了如何把特定的调制解调器初始化为可以在特定操作系统中使用。
包装类,可以用一个对象包装另外一个对象,常见的可以使用:BufferedReader包装一个FileReader,FileReader提供的是底层的读操作,比如Reader(char[] buffer).BufferedReader实现了更高层次上的操作,比如ReadLine.完成读取的是文件中的一行。这就是Decorator模式
一百一十四、你在编码时最常用的设计模式有哪些?在什么场景下用?
1.单例模式:如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
2.策略模式:a、一件事情,有很多方案可以实现。
b、我可以在任何时候,决定采用哪一种实现。
c、未来可能增加更多的方案。
d、策略模式让方案的变化不会影响到使用方案的客户。
3.观察者模式:a、对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
b、对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
4.迭代器模式:当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍 历的时候,就应该考虑 用迭代器模式。其实stl容器就是很好的迭代器模式的例子。
5.工厂模式:a、 在编码时不能预见需要创建哪种类的实例。
b、 系统不应依赖于产品类实例如何被创建、组合和表达的细节
一百一十五、如何实现一个单例?代理模式(动态代理)单例模式(懒汉模式,并发初始化如何解决,volatile与lock的使用)
单例模式(一共8种):饿汉式(2)、懒汉式(3)、枚举、双重检查、静态内部类
饿汉(线程安全):创建一个私有的静态的对象,构造方法私有化,对外提供一个静态get对象的方法。
懒汉(线程不安全):静态声明一个对象,构造方法私有化,提供静态get方法,if(对象==null)就创 建,if条件外边返回对象;
懒汉(线程安全):在get方法上加上synchronized;
静态类内部加载(线程安全):私有的静态内部类里创建方法,构造方法私有化,提供静态get方法并且 在方法里返回对象。
枚举方法(线程安全):enum SingletonDemo{
INSTENCE;
public void otherMethods(){
System.out.println(“something”);
}
}
代理模式(动态代理)
什么是动态代理:动态代理(以下称代理),利用Java的反射技术(Java Reflection),在运行时创建一 个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象)
(Using Java Reflection to create dynamic implementations of interfaces at runtime)。
动态代理的作用:解决特定问题:一个接口的实现在编译时无法知道,需要在运行时才能实现
实现某些设计模式:适配器(Adapter)或修饰器(Decorator)
面向切面编程:如AOP in Spring
代理的是接口(Interfaces),不是类(Class),更不是抽象类。
单例模式(懒汉模式,并发初始化如何解决,volatile与lock的使用)
多线程下的情况主要问题在于 single = new LazySingle(); 这句,这并非一个原子操作,执行这 句,JVM大概做了俩件事:1.给 LazySingle 分配内存,对内存清零,调用 LazySingle 的构造函 数。2.将 single 指向分配的内存空间(执行完这步 single就为非 null 了)由于CPU运行速度远 比内存快,所以为了优化Java运行速度,在没有数据依赖性的前提下,编译器可能对指令重排序,先 执行CPU自己就可以执行的指令。这种优化对单线程没有影响,对多线程可能产生影响。比如线程一 拿到锁,并先执行了第二步,这里第一步还没执行,线程二抢占了CPU,判断single不为空,返回了 地址,执行出错。(因为 Lazysingle 的构造函数还未执行)解决方法就是将 single 变量声明成 volatile 。
volatile 可以禁止指令重排序。“也就是说,在 volatile 变量的赋值操作后面会有一个内 存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。”
一百一十六、JDK源码里面都有些什么让你印象深刻的设计模式使用,举例看看?
适配器模式:用来把一个接口转化成另一个接口
java.util.Arrays#asList()
javax.swing.JTable(TableModel)
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)
javax.xml.bind.annotation.adapters.XmlAdapter#marshal()
javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()
桥接模式:这个模式将抽象和抽象操作的实现进行了解耦,这样使得抽象和实现可以独立地变化。
AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)
JDBC
组合模式:使得客户端看来单个对象和对象的组合是同等的。换句话说,某个类型的方法同时也接受自 身类型作为参数。
javax.swing.JComponent#add(Component)
java.awt.Container#add(Component)
java.util.Map#putAll(Map)
java.util.List#addAll(Collection)
java.util.Set#addAll(Collection)
装饰者模式:动态的给一个对象附加额外的功能,这也是子类的一种替代方式。可以看到,在创建一个 类型的时候,同时也传入同一类型的对象。
这在JDK里随处可见,你会发现它无处不在,所以下面这个列表只是一小部分。
java.io.BufferedInputStream(InputStream)
java.io.DataInputStream(InputStream)
java.io.BufferedOutputStream(OutputStream)
java.util.zip.ZipOutputStream(OutputStream)
java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap
代理模式:代理模式是用一个简单的对象来代替一个复杂的或者创建耗时的对象。
java.lang.reflect.Proxy
RMI
工厂模式:就是一个返回具体对象的方法。
java.lang.Proxy#newProxyInstance()
java.lang.Object#toString()
java.lang.Class#newInstance()
java.lang.reflect.Array#newInstance()
java .lang.reflect.Constructor#newInstance()
java.lang.Boolean#valueOf(String)
java.lang.Class#forName()