jvm结构原理,GC工作原理
Jvm结构:
Jvm主要包括四个部分:
1、类加载器(ClassLoad)
在JVM启动时或者在类运行时将需要的class加载到JVM中。
类加载时间与过程:
类从被加载到虚拟机内存开始,在到卸载出内存为止,正式生命周期包括了:加载,验证,准备,解析,初始化,使用和卸载7个阶段。其中验证、准备、解析这个三个步骤被统称为连接(linking)。
其中,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的 ,类的加载过程必须按照这种顺序按部就班的“开始”(仅仅指的是开始,而非执行或者结束,因为这些阶段通常都是互相交叉的混合进行,通常会在一个阶段执行的过程中调用或者激活另一个阶段),而解析阶段则不一定(它在某些情况下可以在初始化阶段之后再开始,这是为了支持java语言的运行时绑定)
在以下几种情况下,会对未初始化的类进行初始化:
创建类的实例
对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
当初始化一个类的时候,发现其父类没有被初始化,则需要先初始化父类
当虚拟机启动的时候,用户需要指定一个执行的主类,虚拟机会先初始化这个主类
类实例化和类初始化是两个不同概念:
类实例化:是指创建一个类的实例对象的过程
类初始化:是指类中各个类成员(被static修饰的成员变量)赋初始值的过程,是类生命周期的一个过程
ClassLoader的等级加载机制
Java默认提供的三个ClassLoader
BootStrap ClassLoader:被称为启动类加载机制,是Java类加载层次中最顶层的类加载器,负责加载JDK中核心类库。
Extension ClassLoader:被称为扩展类加载器,负责加载Java的扩展类库,Java虚拟机的实现会提供一个扩展目录,该类加载器在此目录里面查找并加载Java类
AppClassLoader:被称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。一版来说,Java应用的类都 是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。
ClassLoader加载类的原理:
ClassLoader使用的是双亲委托机制来搜索加载类的,每一个ClassLoader实例都有一个父类加载器的引用(不是继承关系,是组合关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它的ClassLoadre实例的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是有上之下一次检查的。首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没有加载到,则把任务转交给Extension ClassLoader试图加载,如果没有加载到,则转交给AppClassLoader进行加载,如果它也没有加载到话,则发挥给委托的发起者,有它到指定的文件系统或网络等URL中加载该类,如果都没有加载到此类,那么抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存中,最后返回这个类的内存中Class对象。
类的加载器双亲委托模型:
2、运行时数据区(内存区)
是在jvm运行的时候操作所分配的内存区。运行时内存区主要分为5个区域:
方法区(Methd Area):用于存储类结构信息的地方,包括常量池,静态变量,构造函数。虽然JVM规范把方法区描述为堆的一个逻辑部分,但它却有个别名(non-heap 非堆)
Java堆(Heap):存储java实例或者对象的地方。这块是GC的主要区域。从存储内容上可以看到Java堆和方法区是被java线程共享的。
Java栈(Stack):java栈总是和线程关联在一起,每当创建一个线程时,jvm就会为这个线程创建一个对应的java栈。在这个Java栈中又会包含多个帧栈,每运行一个方法就创建一个帧栈,由于存储局部变量,操作栈,方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个帧栈在Java栈中入栈和出栈的过程。所以java栈是私有。
程序计数器(PC Register):用于保存当前线程执行的内存地址。由于JVN程序是多线程执行的(线程轮转切换),所以为了保证线程切换回来,还能回复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。
本地方法栈(Native Method Stack):和Java栈的作用差不多,只不过是为JVM使用到的native方法服务的。
3、执行引擎
负责执行class文件中包含的字节码指令
4、本地接口
主要是调用C或C 实现的本地方法及返回结果
JVM内存分配:
Java虚拟机是一次性分配一块较大的内存空间,然后每次new时都在该空间上进行分配和释放,减少了系统调用的次数,节省了一定的开销,这有点类似于内存池的概念。有了这块空间,如何进行分配和回收就跟GC机制有关系了。
Java一般内存申请有两种:静态内存和动态内存。很容易理解,编译时就能够确定的内存就是静态内存,即内存是固定的,系统一次性分配。比如int类型变量;动态内存分配就是在程序执行时才知道要分配的存储空间大小,比如java对象的内存空间。综上所述:java栈、程序计数器、本地方法栈都是线程私有的,线程生就生,线程灭就灭,栈中的栈帧随着方法的结束也会撤销,内存自然就跟着回收了。所以几个地方的内存分配和回收是确定的,不需要管。但是java堆和方法区则不一样,我们只有在程序运行期间才知道会创建哪些对象,所以这部分内存分配和回收是动态的。一般说的垃圾回收也是针对这部分的。
垃圾检测和回收算法
垃圾收集器一般必须完成两件事:检测出垃圾,回收垃圾。检测垃圾一般分为以下几种:
引用计数法:给对象增加一个引用计数器,每当有地方引用这个对象时,就在计数器上加1,引用失效就减1
可达性分析算法:以根集对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象。这里的根集一般包括java堆中引用的对象,方法区常量池的引用对象。
总之,jvm在做垃圾回收的时候,会检查堆中的所有对象是否会被这些根集对象引用,不能够被引用的对象就会被垃圾收集器回收。一般回收有一下几种方法:
1、标记-清除(Mark-sweep):分为两个阶段,标记和清楚。标记所有需要回收的对象,然后统一回收这个是最基础的算法,后续的收集算法 都是基于这个算法扩展的。
不足:效率低;标记清楚之后会产生大量的碎片。
2、复制(copying): 此算法把内存空间划分为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前区域,把正在使用中的对象复制到另外区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去还能进行相应的内存整理,不会出现“内存碎片”问题。当然,此算法的缺点也是很明显,就是需要双倍内存。
3、标记-整理(Mark-Compact):此算法结合了“标记-清除”和“复制”两个算法的有点。也是两个阶段,第一阶段从根节点开始标记所被引用的对象。第二阶段遍历整个堆,把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“复制”算法的空间问题
4、分代收集算法:这是当前商业虚拟机常用的垃圾收集算法。分代的垃圾回收策率,是基于这样一个事实:不同的对象的生命周期不一样的。因此,不同生命周期的对象采取不同的收集方式,以便提高回收效率。
为什么要运用分代垃圾回收策率?在java程序运行的过程中,会产生大量的对象,因每个对象所能承担的职责和功能不同,所以也有着不同的生命周期。有的对象生命周期较长,有的对象生命周期较短。试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,那么消耗的时间相对比较长,而对于存活时间较长的对象进行扫描的工作是徒劳的。因此就引入了分治思想,所以分治思想就是因地制宜,将对象进行代划分,把不同的生命周期的对象放在不同的代上使用不同的垃圾回收方法。
如果划分?将对象按其生命周期的不同划分成:年轻代(Young Generation)、老年代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是类信息,所以与java对象的回收关系不大,与回收信息相关的是年轻代,老年代。
年轻代:是所有新对象产生的地方。年轻代被分为3个部分:Ender(出生)区和两个Survivor(幸存者)区(From和To)。当Ender区被对象填满时,就会执行Minor GC。并把所有存活下来的对象转移到其中一个Survivor区(假设为from区)。Minor GC同样会检查存活下来的对象,并把他们转移到另一个Survivor区(假设为to区)。这样在一段时间内总会有一个空的Survivor区。经过多次GC后,仍然存活下来的对象会被转移到老年代内存空间。
年老代:在年轻代经过N次回收的仍然没有被清除的对象会放到老年代,可以说它们是久经沙场而不亡的一代,都是生命周期较长的对象。对于老年代和永久代,就不能再采用年轻代中那样搬移腾挪的回收算法,因为那些对于这些回收战场上的老兵来说是小儿科,通常会在老年代内存被占满时将会触发Full GC,回收整个堆内存。
持久代:用于存放静态文件,比如Java类,方法等。持久代对垃圾回收没有显著的影响。
我这里之所以最后讲分代,是因为分代里涉及了前面几种算法。年轻代:涉及了复制算法;年老代:涉及了“标记-整理(Mark-Sweep)”的算法。
Java线程池
java中的ThreadPoolExecutor类
ThreadPoolExecutor类继承了AbstractExecutorService类,并提供了四个构造器。构造器中分别有一下参数:
corePoolSize:核心池的大小,在创建线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法来预创建线程。即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
maximumPoolSize:线程池最大的线程数,表示在线程池中最多能创建多少个线程;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize。即当线程池中的线程数大于corePoolSize时,如果一个线程空闲时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,知道线程池中的线程数为0。
unit:参数keepAliveTime的时间单位,有7中取值,在TimeUnit类中有7中静态属性:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一版来说阻塞队列有以下几种:
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
ArrayBlockingQueue使用较少,一使用LinkedBlokingQueue和SynchronousQueue。线程池排队策率与BlockingQueue有关
threadFactory:线程工厂,主要用来创建线程;
hander:表示当拒绝处理任务时的策率,有一下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
ThreadPoolExecutor.DiscardOlddestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:有调用线程处理该任务
ThreadPoolExecutor继承了AbstractExecutorService抽象类,AbstractExecutorService抽象类实现了ExecutorService接口,ExecutorService接口继承了Executor接口。
Executor:是一个顶层接口,在它里面只有一个方法execute(Runnable),返回值为void,参数为Runnable类型,其实就是为了执行传进来的任务;
然后ExecutorService接口继承了Executor接口,并生命了一些方法:submit、invokeAll、invokeAny以及shutDown等。
抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService接口中所有的方法;
然后ThreadPoolExecutor继承了抽象类AbstractExecutorService;
在ThreadPoolExecutor类中有几个非常重要的方法:
execute():实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交线程池去执行。
submit():此方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和executor()方法不同,它能够返回任务执行的结果,去看submit()方法的方法实现,会发现它实际上还是调用的execute()方法,只不过它利用了Futrure来获取任务的执行结果
shutDown():是用来关闭线程
shutDownNow():是用来关闭所有线程
webservice是什么?
webservice是一种跨编程语言和跨操作系统的远程调用技术,遵循SOPA/WSDL规范。
springCloud是什么?
springcloud是一个微服务框架,并提供全套分布式系统解决方案。支持配置管理,熔断机制,leader选举,服务治理,分布式session,微代理,控制总线,智能路由,一次性token。
说一下springIOC
IOC控制反转也叫依赖注入,我理解的是它就是一个生产和管理bean的一个容器,你自己不需要通过new去生成对象而是通过spring bean的工厂来生成所需要的对象。
原理
将对象交给容器管理,你只需要在spring配置文件中配置对应的bean以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在A里面new这些bean了。
说一下springIOC的几种注入方式
1.通过注解注入
2.调用set方法注入
3.通过构造方法注入
Java中堆和栈有什么不同?
每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。
堆:(对象)
引用类型的变量,其内存分配在堆上或者常量池(字符串常量、基本数据类型常量),需要通过new等方式来创建。
堆内存主要作用是存放运行时创建(new)的对象。
(主要用于存放对象,存取速度慢,可以运行时动态分配内存,生存期不需要提前确定)
栈:(基本数据类型变量、对象的引用变量)
基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以及对象的引用变量,其内存分配在栈上,变量出了作用域就会自动释放。
说一下springAOP
简单来说,就是将那些与业务无关,却为业务模块共同调用的逻辑封装起来,减少重复代码降低了代码的耦合度。并有利于未来对代码的可操作性和可维护性。
原理
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码
Spring的Scope有以下几种,通过@Scope注解来实现:
(1)Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例。
(2)Prototype:每次调用新建一个Bean实例。
(3)Request:Web项目中,给每一个 http request 新建一个Bean实例。
(4)Session:Web项目中,给每一个 http session 新建一个Bean实例。
(5)GlobalSession:这个只在portal应用中有用,给每一个 global http session 新建一个Bean实例。
Spring事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
- TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
Spring的声明式事务管理力度是什么级别?
Struts2是类级别的,Spring是方法级别的
spring事务可以分为编程式事务和声明式事务
spring常用接口
1.ApplicationListener:当需要监听自定义事件时,可以新建一个实现ApplicationListener接口的类,并将该类配置到Spring容器中。
2.FactoryBean:用于创建特定的对象,对象的类型由getObject方法的返回值决定。
3.ApplicationContextAware:当一个类需要获取ApplicationContext实例时,可以让该类实现ApplicationContextAware接口。
4.ApplicationEvent:当需要创建自定义事件时,可以新建一个继承自ApplicationEvent抽象类的类。
spring MVC与struts2的区别:
参考: http://blog.csdn.net/chenleixing/article/details/44570681
① Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截
② SpringMVC的方法之间基本上独立的,独享request response数据
③ 由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的
④ 拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式
⑤ SpringMVC的入口是servlet,而Struts2是filter
⑥ SpringMVC集成了Ajax
⑦ SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱
⑧ Spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高
⑨ Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展
⑩ SpringMVC开发效率和性能高于Struts2
Spring框架中的核心思想包括什么?
主要思想是IOC控制反转,DI依赖注入,AOP面向切面
ArrayList和LinkedList的大致区别如下:
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。 3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
ArrayList,Vector主要区别为以下几点:
(1):Vector是线程安全的,源码中有很多的synchronized可以看出,而ArrayList不是。导致Vector效率无法和ArrayList相比;
(2):ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加为原来的一倍;
HashSet与HashMap的区别:
HashMap和Hashtable的区别:
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。
- HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
- HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
- 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
- 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
- HashMap不能保证随着时间的推移Map中的元素次序是不变的。
线程安全是什么?线程不安全是什么?
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。(Vector,HashTable) 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。(ArrayList,LinkedList,HashMap等)
线程和进程的区别?
进程和线程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同;
(1)进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元 (2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。 (3)进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束 (4)线程是轻两级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的 (5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源 (6)线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志
线程池的作用:
线程池是为了限制系统中执行线程的数量
根据系统环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其它线程排队等候。一个任务执行完毕,再从队列中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
为什么要用线程池:
减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,增加服务器的压力(每个线程大约需要1MB的内存,线程开的越多,消耗内存越大,最后导致死机);
黑盒测试、灰盒测试、白盒测试、单元测试有什么区别?
黑盒测试关注程序的功能是否正确,面向实际用户;
白盒测试关注程序源代码的内部逻辑结构是否正确,面向编程人员;
灰盒测试是介于白盒测试与黑盒测试之间的一种测试。
单元测试(Unit Testing)是对软件基本组成单元进行的测试,如函数或是一个类的方法。这里的单元,就是软件设计的最小单位。
怎么对数据库百万级数据进行优化?
使用读写分离技术(
让主数据库(master)处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库(slave)处理SELECT查询操作 )
Spring Bean的生命周期:
- Bean的建立, 由BeanFactory读取Bean定义文件,并生成各个实例
- Setter注入,执行Bean的属性依赖注入
- BeanNameAware的setBeanName(), 如果实现该接口,则执行其setBeanName方法
- BeanFactoryAware的setBeanFactory(),如果实现该接口,则执行其setBeanFactory方法
- BeanPostProcessor的processBeforeInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processBeforeInitialization()方法
- InitializingBean的afterPropertiesSet(),如果实现了该接口,则执行其afterPropertiesSet()方法
- Bean定义文件中定义init-method
- BeanPostProcessors的processAfterInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processAfterInitialization()方法
- DisposableBean的destroy(),在容器关闭时,如果Bean类实现了该接口,则执行它的destroy()方法
- Bean定义文件中定义destroy-method,在容器关闭时,可以在Bean定义文件中使用“destory-method”定义的方法
简单回答springbean生命周期:
(1)实例化(必须的)构造函数构造对象
(2)装配(可选的)为属性赋值
(3)回调(可选的)(容器-控制类和组件-回调类)
(4)初始化(init-method=" ")
(5)就绪
(6)销毁(destroy-method=" ")
springmvc生命周期:
1A)客户端发出http请求,只要请求形式符合web.xml 文件中配置的*.action的话,就由DispatcherServlet 来处理。
1B)DispatcherServlet再将http请求委托给映射器 的对象来将http请求交给对应的Action来处理
2)映射器根据客户的http请求,再对比<bean name="/hello.action 如果匹配正确,再将http请求交给程序员写的Action
3)执行Action中的业务方法,最终返回一个名叫ModelAndView 的对象,其中封装了向视图发送的数据和视图的逻辑名
4)ModelAndView对象随着响应到到DispatcherServlet中了
5)这时DispatcherServlet收到了ModelAndView对象, 它也不知道视图逻辑名是何意,又得委托一个名叫 视图解析器的对象去具体解析ModelAndView对象 中的内容
6)将视图解析器解析后的内容,再次交由DispatcherServlet 核心控制器,这时核心控制器再将请求转发到具体的 视图页面,取出数据,再显示给用户
servlet生命周期?
Servlet 通过调用 init () 方法进行初始化。 Servlet 调用 service() 方法来处理客户端的请求。 Servlet 通过调用 destroy() 方法终止(结束)。 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的
ajax怎么解决跨域?
参考:http://blog.csdn.net/u014727260/article/details/72793459
① 代理(通过后台操作)
② JSONP(添加响应头,允许跨域 )
addHeader(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
addHeader(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式
③ 在ajax的dataType方式改为“jsonp”
Mysql数据类型:
① 普通索引
② 唯一索引
③ 主键索引
④ 组合索引
⑤ 全文索引
参考:https://www.cnblogs.com/luyucheng/p/6289714.html
Eureka和zookeeper的区别?
① 做分布式下的服务发现还是使用eureka更好,也就是AP特性的分布式协调工具(zookeeper因为网络故障就无法返回可用的主机)
② zookeeper技术更加成熟,资料更多
③ Eureka。是spring cloud之下一个专门负责微服务服务注册和发现的组件,Eureka就是为了服务发现而设计的
④ Zookeeper。是用来保证分布式一致性的一个软件。不是为了服务发现注册而设计的,只不过它的特性也可以被二次开发成服务发现注册中心罢了
SpringCloud都有哪些组件?
Spring Cloud为微服务架构开发涉及的配置管理,服务治理,熔断机制,智能路由,微代理,控制总线,一次性token,全局一致性锁,leader选举,分布式session,集群状态管理等操作提供了一种简单的开发方式。
组件列:
- Spring Cloud Config:配置管理工具,支持使用Git存储配置内容,支持应用配置的外部化存储,支持客户端配置信息刷新、加解密配置内容等
- Spring Cloud Bus:事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。
- Spring Cloud Netflix:针对多种Netflix组件提供的开发工具包,其中包括Eureka、Hystrix、Zuul、Archaius等。
- Netflix Eureka:一个基于rest服务的服务治理组件,包括服务注册中心、服务注册与服务发现机制的实现,实现了云端负载均衡和中间层服务器的故障转移。
- Netflix Hystrix:容错管理工具,实现断路器模式,通过控制服务的节点,从而对延迟和故障提供更强大的容错能力。
- Netflix Ribbon:客户端负载均衡的服务调用组件。
- Netflix Feign:基于Ribbon和Hystrix的声明式服务调用组件。
- Netflix Zuul:微服务网关,提供动态路由,访问过滤等服务。
- Netflix Archaius:配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。
- Spring Cloud for Cloud Foundry:通过Oauth2协议绑定服务到CloudFoundry,CloudFoundry是VMware推出的开源PaaS云平台。
- Spring Cloud Sleuth:日志收集工具包,封装了Dapper,Zipkin和HTrace操作。
- Spring Cloud Data Flow:大数据操作工具,通过命令行方式操作数据流。
- Spring Cloud Security:安全工具包,为你的应用程序添加安全控制,主要是指OAuth2。
- Spring Cloud Consul:封装了Consul操作,consul是一个服务发现与配置工具,与Docker容器可以无缝集成。
- Spring Cloud Zookeeper:操作Zookeeper的工具包,用于使用zookeeper方式的服务注册和发现。
- Spring Cloud Stream:数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息。
- Spring Cloud CLI:基于 Spring Boot CLI,可以让你以命令行方式快速建立云组件
Hibernate的三种状态是什么?怎么将游离状态转换为持久化状态?
transient(瞬时状态),persistent(持久化状态)以及detached(离线状态)
转换:update() saveOrUpdate() lock()
dubbo
一丶简单说一下dubbo
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,就是个远程服务调用的分布式框架(告别Web Service模式中的WSdl,以服务者与消费者的方式在dubbo上注册)
其核心部分包含: 1. 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。 2. 集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。 3. 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
二丶dubbo调用关系说明
(1)服务提供者在启动时,向注册中心注册自己提供的服务。
(2)服务消费者在启动时,向注册中心订阅自己所需的服务。
(3)注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
(4)服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
(5)服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
三丶你配置过dubbo哪些东西
代码语言:javascript复制# 应用名
dubbo.application.name = dubbodemo-consumer
# 注册中心地址
dubbo.registry.address = zookeeper://localhost:2181
# 调用协议地址
dubbo.protocol.name = dubbo
dubbo.protocol.port = 28080
代码语言:javascript复制# 设置服务端和消费端连接超时时间
spring.dubbo.consumer.timeout=30000
spring.dubbo.provider.timeout=30000
代码语言:javascript复制消费端要配置调用哪些服务
谈谈final, finally, finalize的区别。 final?修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载 。
finally?再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。
finalize?方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。
HashMap和Hashtable的区别。 都属于Map接口的类,实现了将惟一键映射到特定的值上。 HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。 Hashtable 类似于 HashMap,但是不允许 null 键和 null 值。它也比 HashMap 慢,因为它是同步的。
String s = new String("xyz");创建了几个String Object?
两个对象,一个是“xyx”,一个是指向“xyx”的引用对象s。
sleep() 和 wait() 有什么区别? 搞线程的最爱
sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级 。 (b)正在运行的线程因为其它原因而阻塞。 wait()是线程交互时,如果线程对一个同步对象x 发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。
short s1 = 1; s1 = s1 1;有什么错? short s1 = 1; s1 = 1;有什么错?
short s1 = 1; s1 = s1 1;有错,s1是short型,s1 1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 1) 。short s1 = 1; s1 = 1正确。
Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?
方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。 equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
error和exception有什么区别?
error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
给我一个你最常见到的runtime exception。
代码语言:javascript复制ArithmeticException, ArrayStoreException,
BufferOverflowException, BufferUnderflowException,
CannotRedoException, CannotUndoException,
ClassCastException, CMMException,
ConcurrentModificationException, DOMException,
EmptyStackException, IllegalArgumentException,
IllegalMonitorStateException, IllegalPathStateException,
IllegalStateException, ImagingOpException,
IndexOutOfBoundsException, MissingResourceException,
NegativeArraySizeException, NoSuchElementException,
NullPointerException, ProfileDataException,
ProviderException, RasterFormatException, SecurityException,
SystemException, UndeclaredThrowableException,
UnmodifiableSetException, UnsupportedOperationException
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。 equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
JAVA中的几种基本类型
数据库会死锁吗,举一个死锁的例子,mysql 怎么解决死锁。
产生死锁的原因主要是:
(1)系统资源不足。
(2) 进程运行推进的顺序不合适。
(3)资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
这里提供两个解决数据库死锁的方法:
1)重启数据库(谁用谁知道)
2)杀掉抢资源的进程:
先查哪些进程在抢资源:SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
杀掉它们:Kill trx_mysql_thread_id;
高并发下,如何做到安全的修改同一行数据。
使用悲观锁 悲观锁本质是当前只有一个线程执行操作,结束了唤醒其他线程进行处理。
也可以缓存队列中锁定主键。
乐观锁和悲观锁是什么,INNODB 的行级锁有哪 2 种,解释其含义。
乐观锁是设定每次修改都不会冲突,只在提交的时候去检查,悲观锁设定每次修改都会冲突,持有排他锁。
行级锁分为共享锁和排他锁两种 共享锁又称读锁 排他锁又称写锁
数据库隔离级别有哪些,各自的含义是什么,MYSQL 默认的隔离级别是是什么。
·未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
·提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
·可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
·串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
MYSQL默认是RepeatedRead级别
ACID 是什么。
A,atomic,原子性,要么都提交,要么都失败,不能一部分成功,一部分失败。
C,consistent,一致性,事物开始及结束后,数据的一致性约束没有被破坏
I,isolation,隔离性,并发事物间相互不影响,互不干扰。
D,durability,持久性,已经提交的事物对数据库所做的更新必须永久保存。即便发生崩溃,也不能被回滚或数据丢失。
##Mysql 怎么优化 table scan 的。
避免在where子句中对字段进行is null判断
应尽量避免在where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
避免在where 子句中使用or 来连接条件
in 和not in 也要慎用
Like查询(非左开头)
使用NUM=@num参数这种
where 子句中对字段进行表达式操作num/2=XX
在where子句中对字段进行函数操作
如何写 sql 能够有效的使用到复合索引。
由于复合索引的组合索引,类似多个木板拼接在一起,如果中间断了就无法用了,所以要能用到复合索引,首先开头(第一列)要用上,比如index(a,b) 这种,我们可以select table tname where a=XX 用到第一列索引 如果想用第二列 可以 and b=XX 或者and b like‘TTT%’
mysql 中 in 和 exists 区别。
mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。
如果查询的两个表大小相当,那么用in和exists差别不大。
如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:
not in 和not exists如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。
1.EXISTS只返回TRUE或FALSE,不会返回UNKNOWN。
2.IN当遇到包含NULL的情况,那么就会返回UNKNOWN。
session与cookie的差别
1,session 在服务器端,cookie 在客户端(浏览器)
2,session 默认被存在在服务器的一个文件里(不是内存)
3,session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 session_id)
4,session 可以放在 文件、数据库、或内存中都可以。
5,用户验证这种场合一般会用 session
因此,维持一个会话的核心就是客户端的唯一标识,即 session id
分布式事务
在微服务架构下,能不使用分布式事务就尽量不要使用,如果一定要使用的话,目前主流的方案有三种:
1.1 二阶段提交 2PC,强一致性
通过提交分阶段和记日志的形式,记录下事务提交所处的阶段状态,在组件宕机重启后,可通过日志恢复事务提交的阶段状态,并在这个阶段节点重试。
1.2 消息机制,最终一致性
借助消息队列,在处理业务逻辑的地方,发送消息,业务逻辑处理成功后,提交消息,确保消息是发送成功的。之后消息队列投递来进行处理,如果成功,则结束,如果没有成功,则重试,直到成功。不过仅仅适用业务逻辑中,第一阶段成功,第二阶段必须成功的场景。
1.3 TCC补偿模式,最终一致性
服务器A的事务如果执行顺利,那么事务A就先行提交,如果事务B也执行顺利,则事务B也提交,整个事务就算完成。但是如果事务B执行失败,事务B本身回滚,这时事务A已经被提交,所以需要执行一个补偿操作,将已经提交的事务A执行的操作反操作,恢复到未执行事务A前的状态。
2 Spring Boot和Spring Cloud的关系
Spring Boot是快速开发工具,专注于应用个体;Spring Cloud基于Spring Boot开发,因此继承了Spring Boot的特性,其更专注于服务治理。
3 Spring Cloud和Dubbo的区别
Dubbo关注的领域是Spring Cloud的一个子集。Dubbo专注于服务治理,其在服务治理、灰度发布、流量分发方面比Spring Cloud更全面。Spring Cloud覆盖整个微服务架构领域。
Dubbo使用RPC调用效率高一些,Spring Cloud使用HTTP调用效率低,使用更简单。
4 REST和RPC的区别
REST风格的系统交互更方便,RPC调用服务提供方和调用方式之间依赖太强。
REST调用系统性能较低,RPC调用效率比REST高。
REST的灵活性可以跨系统跨语言调用,RPC只能在同语言内调用。
REST可以和Swagger等工具整合,自动输出接口API文档。
5 Eureka续约与剔除
服务实例启动后,会周期性地向 Eureka Server 发送心跳以续约自己的信息,避免自己的注册信息被剔除。续约的方式与服务注册基本一致:首先更新自身状态,再同步到其它Peer。
如果Eureka Server在一段时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(自我保护模式除外)。
6 Eureka自我保护机制
默认情况下,如果Eureka Server在90s内没有接收到某个微服务实例的心跳,会注销该实例。但是在微服务架构下服务之间通常都是跨进程调用,网络通信往往会面临着各种问题,导致此实例被注销。当Eureka Server检测到大规模服务异常时,会自动进入自我保护机制:
Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
Eureka仍然能够接收新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点依然可用)。
当网络稳定时,当前实例新的注册信息会被同步到其他节点中。
7 Eureka和Consul的区别
Consul服务注册相比Eureka会稍慢一些。因为 Consul的raft协议要求必须过半数的节点都写入成功才认为注册成功,Leader挂掉时,重新选举期间整个Consul不可用。Eureka服务注册快,但可能会出现数据不一致的情况。
8 Ribbon和Feign的区别
Ribbon是一个基于HTTP和TCP客户端的负载均衡器。它可以在客户端配置ribbonServerList(服务端列表),然后轮询请求以实现负载均衡。Feign是在Ribbon的基础上进行了一次封装,是一个使用起来更加方便的HTTP客户端,让调用者更像是调用一个本地方法一样调用远程服务。
其中OpenFeign是在Netflix Feign的基础上扩展了对Spring MVC的注解支持,在新版本的Spring Cloud中已经没有了对Feign的依赖集成。
9 服务熔断和服务降级
熔断机制是应对雪崩效应的一种微服务链路保护机制。当某个微服务不可用或者响应时间太长时,会进行服务降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在Spring Cloud框架里熔断机制通过Hystrix或Resilience4j来实现。
服务降级,当某个服务熔断之后,客户端将不再调用此请求,此时客户端返回一个本地的fallback回调,返回一个缺省值。这样做,虽然会出现局部的错误,但可以避免因为一个服务挂机,而影响到整个架构的稳定性。
10 服务网关的作用
简化客户端调用复杂度,统一处理外部请求。
数据裁剪以及聚合,根据不同的接口需求,对数据加工后对外。
多渠道支持,针对不同的客户端提供不同的网关支持。
遗留系统的微服务化改造,可以作为新老系统的中转组件。
统一处理调用过程中的安全、权限问题。
Spring Cloud中的网关有:Zuul和Spring Cloud Gateway,最新版本中推荐使用后者。
11 Spring Cloud Bus的作用
Spring Cloud Bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring Cloud Bus的一个核心思想是通过分布式的启动器对Spring Boot应用进行扩展,也可以用来建立一个多个应用之间的通信频道。
12 链路跟踪Sleuth
当我们项目中引入Spring Cloud Sleuth后,每次链路请求都会添加一串追踪信息,格式是[server-name, main-traceId,sub-spanId,boolean]:
server-name:服务结点名称。
main-traceId:一条链路唯一的ID,为TraceID。
sub-spanId:链路中每一环的ID,为SpanID。
boolean:是否将信息输出到Zipkin等服务收集和展示。
Sleuth的实现是基于HTTP的,为了在数据的收集过程中不能影响到正常业务,Sleuth会在每个请求的Header上添加跟踪需求的重要信息。这样在数据收集时,只需要将Header上的相关信息发送给对应的图像工具即可,图像工具根据上传的数据,按照Span对应的逻辑进行分析、展示。