0,基本数据类型:html
byte:Java中最小的数据类型,在内存中占8位(bit),即1个字节,取值范围-128~127,默认值0 short:短整型,在内存中占16位,即2个字节,取值范围-32768~32717,默认值0 int:整型,用于存储整数,在内在中占32位,即4个字节,取值范围-2147483648~2147483647,默认值0 long:长整型,在内存中占64位,即8个字节-2^63~2^63-1,默认值0L float:浮点型,在内存中占32位,即4个字节,用于存储带小数点的数字(与double的区别在于float类型有效小数点只有6~7位),默认值0 double:双精度浮点型,用于存储带有小数点的数字,在内存中占64位,即8个字节,默认值0 char:字符型,用于存储单个字符,占16位,即2个字节,取值范围0~65535,默认值为空 boolean:布尔类型,占1个字节,用于判断真或假(仅有两个值,即true、false),默认值false前端
1,Java的引用类型: 强引用、弱引用、软引用、虚引用 1,强引用是使用最广泛的引用。若是一个对象具备强引用,那垃圾回收器毫不会回收它。 2,若是一个对象只具备软引用,则内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高速缓存。 3,弱引用与软引用的区别在于:只具备弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。 4, “虚引用”顾名思义,就是形同虚设,与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。 虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,若是发现它还有虚引用,就会在回收对象的内存以前,把这个虚引用加入到与之 关联的引用队列中java
1,WeakReference如字面意思,弱引用, 当一个对象仅仅被weak reference(弱引用)指向, 而没有任何其余strong reference(强引用)指向的时候, 若是这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收 2,若是一个对象只具备软引用(SoftReference),则内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高速缓存node
3,ThreadLocal何时出现内存泄漏?ThreadLocal里面为啥使用了WeakReference? Thread实例为每一个ThreadLocal对象维护了一个副本,这个副本数据存放在ThreadLocalMap里面,所以才作到线程间的数据不共享。 <1>当一个ThreadLocal实例被直接赋值为null(没有调用set,remove),此时会出现内存泄漏,由于thread实例里面的ThreadLocalMap保存了ThreadLocal的引用,假设此时线程没有被销毁,所以在gc的时候并不能回收这部分空间,就是说出现了内存泄漏(ThreadLocal直接赋值为null的方式,不管使用强弱引用都没法解决内存泄漏的问题)。 <2>若是使用弱引用(实际是ThreadLocalMap的Entry类的key才使用弱引用,value没有使用,ThreadLocalMap里面放就是Entry弱引用,其封装了ThreadLocal),在ThreadLocal对象被赋值为null,会致使弱引用在gc的时候,Entry的key被回收并变成null,使用弱引用能够多一层保障:对应的value在下一次ThreadLocalMap调用set,get,remove的时候会被清除(这些方法的内部对Entry的key为null的value数据进行清除)。c++
备注:ThreadLocal是一个类,当实例化一个ThreadLocal对象时,会在当前线程Thread建立一个ThreadLocalMap,这个ThreadLocalMap里面存放了Entry,Entry是由ThreadLocal(key)和value(实际的数据)构成。Entry的key是经过弱引用封装,若是ThreadLocal没有外部指向(即被赋值为null)时,那Entry的key在gc的时候就会被回收,当此线程的ThreadLocalMap被再次访问时,会自动删除之前Entry的key为null的value数据。面试
参考:blog.csdn.net/wudiyong22/…算法
4,内存溢出和内存泄漏的区别 内存溢出(Out Of Memory,OOM),就是内存不够用了,内存泄漏(Memory Leak),指的是申请的内存空间,本身没有去主动释放,gc也没法释放(如强引用),屡次内存泄漏,就会致使内存溢 memory leak会最终会致使out of memory!spring
2,Arraylist初始容量为10,每次扩容1.5倍,原来元素拷贝过去,hashMap初始化容量是16,负载因子0.75,每次容量达到(0.75*上次容量)开始扩容2倍sql
3,线程池核心线程大小设置,机器内核数量,qps,相应时间关系 <1>若是是计算密集型的服务,因为cpu处理效率很是高,核心线程通常设置为内核N+1(1为等待cpu的时间片) <2>若是是io耗时较高的服务,通常设置为(qps*99线)/1000,其中99线为毫秒数据库
4,简述线程池的实现:线程池把每一个提交到线程池的任务封装成一个worker(而且实现了Runnable接口),当第一批任务到达的时候(corePool还没到最大值),此时new出的线程开始执行任务,执行完,而且去消费队列,若是coreSize满了,此时队列就有值了,这时候就会消费队列里面的任务了,其实是利用阻塞队列的take方法维持核心线程的存活,若是队列满了,就会建立新线程,直至达到maxSizePool,在消费队列中的任务数据的同时,若是线程在keepAlive时间范围内获取不到队列数据,就会释放最大线程,是经过workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)控制非核心线程的存活,若是从队列获取不到数据,就从worker集合删除该线程。
5,信号量的使用:把信号量比做资源,容许多线程去使用资源,可是只能容许部分线程使用。semaphore构造方法初始化资源大小,semaphore.acquire()获取资源,semaphore.release()释放资源。 CountDownLatch和Semaphore底层实现都是基于AbstractQueuedSynchronizer,CountDownLatch和Semaphore属于典型的共享锁。CyclicBarrier用来给多个线程之间进行互相等待其余全部线程而设计的(而且实现了可重用机制,每次计数到0,自动从新设置为起始值),而CountDownLatch是给一个起"调度"其它一组线程用的,这个线程关键职责就是等待进行其余工做线程返回。
备注:CountDownLatch.await阻塞主线程,CountDownLatch.countDown计数变量递减,递减到0会唤醒主线程,cyclicBarrier.await()当作栅栏,让一组线程都在栅栏以前完成任务,内部会作计数,只有变成0,才能让全部线程复活。 CountDownLatch的计数器没法被重置;CyclicBarrier的计数器能够被重置后使用,所以它被称为是循环的barrier。
使用场景:CountDownLatch是一个线程等待一组子线程执行完任务,再往下执行其余逻辑,cyclicBarrier是一组线程都达到一个临界值,再开始作新的任务。
6,在不少状况下,可能有多个线程须要访问数目不多的资源。假想在服务器上运行着若干个回答客户端请求的线程。这些线程须要链接到同一数据库,但任一时刻只能得到必定数目的数据库链接。你要怎样才可以有效地将这些固定数目的数据库链接分配给大量的线程? 解决方案:好比把资源放到阻塞队列,或者放到信号量里面。
7,ConcurrentHashMap的原理 1.6和1.7的实现:分为了16个segement,每一个segement内部再次实现一次hashmap,所以查找一个数据须要两次hash(先找segement,再找segement里面的hash位置),put操做是在segement维度使用了reentrantlock,get是先找到segement,再查找数据,找到就对整个segement加锁。size方法是比较两次结果,若是不相等,就对每一个segement加锁,从新计算(为何要比较两次?在两次比较的过程当中,被修改的几率很小,若是要加锁,就会致使整个map锁大量竞争(读多写少的时候),不如一开始不用锁的方式进行比较)。 1.8:再也不使用segement,直接使用node数组+链表,当链表长度达到8,会升级为红黑树。put操做是使用了synchronize对当前链表加锁,get是使用Unsafe.getObjectVolatile获取最新的共享内存值(不加锁)。
9,Object的notify 和 notifyAll的区别 notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。因此若是有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪一个线程取决于操做系统对多线程管理的实现。notifyAll 会唤醒全部等待(对象的)线程,尽管哪个线程将会第一个处理取决于操做系统的实现。若是当前状况下有多个线程须要被唤醒,推荐使用notifyAll 方法。好比在生产者-消费者里面的使用,每次都须要唤醒全部的消费者或是生产者,以判断程序是否能够继续往下执行。
notify只是唤醒正在等待的线程,至于何时开始竞争,取决于当前线程何时释放。(ReentrantLock对应的condition.signal方法也是如此)
10,可重入锁(ReentrantLock)的使用场景:当前线程内部逻辑进行递归调用
11,synchronized(独占锁),多线程使用,结合object的wait、notify(notifyAll)使用时注意的问题,调用wait,是释放当前线程持有某个对象的锁,让给其它线程竞争,而且由它们通知回调。 备注:使用wait、notify(notifyAll)方法前提必须是当前线程持有锁,也就是说必须在synchronized模块内使用 synchronized的锁标记存放在Java对象头的Mark Word中,同步代码块采用monitorenter、monitorexit指令(c++层面)显式的实现。
12,ReentrantLock(独占锁),多线程使用,结合Condition(condition = myLock.newCondition()),condition.await()和signal、signalAll()通知其它线程进行锁的竞争。 备注:1,使用await、signal(signalAll)方法前提必须是当前线程持有锁(也就是说一个线程不能释放别的线程持有的锁) 2,reentrantlock的lock方法若是获取不到锁,会被阻塞,tryLock获取不到,马上返回false,tryLock(long time, TimeUnit unit)是对获取锁加上时间控制 3,condition.await(),将一个线程的锁交出,当前线程进入挂起状态(cpu时间片交出),当前线程放入等待锁的双向队列(AQS)里面,这个线程同时也被另一个condition队列维护,condition.signal()调用 时,将双向队列中的线程设置为可抢锁状态,condition队列的头结点删除此线程数据。 4,condition.await(),是由用户的其它线程唤醒,condition.await(time),这是由内核在指定时间后去帮你唤醒的
13,静态代理和动态代理区别 静态不够灵活,须要针对每一个被代理类的接口都对应开发一个代理类的接口,代码维护成本比较高。 14,动态代理实现的两种方式和区别 java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,经过修改其字节码生成子类来处理
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
15,CGlib比JDK代理快?
(1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。惟一须要注意的是,CGLib不能对声明为final的方法进行代理,由于CGLib原理是动态生成被代理类 的子类。 (2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。
16,Java 序列化作了哪些事情 Java的序列化算法通常会按步骤作以下事情: ◆将对象实例相关的类元数据输出。 ◆递归地输出类的超类描述直到再也不有超类。 ◆类元数据完了之后,开始从最顶层的超类开始输出对象实例的实际数据值。 ◆从上至下递归输出实例的数据
17,简述公平锁和非公平锁的实现。 Reentrantlock支持公平和非公平模式,实现锁的最基础组件类是:内部类NonfairSync和FairSync,外部AbstractQueuedSynchronizer(抽象队列同步器,AQS),公平锁和非公平锁在获取锁时都尝试性去获取,当获取失败才进入有序等待队列中(先进先出的双向链表),而且这些线程会被挂起(让出cpu时间片),公平锁在获取锁时,会判断当前线程是不是队列头结点线程(hasQueuedPredecessors),若是是头结点才有权拿到锁。非公平锁在获取锁时,是没在队列中的线程和队列的头结点竞争(即获取锁时,不对线程是不是头结点线程作限制)。当一个锁被释放时,它会去唤醒等待队列中的头结点,所以才出现新来线程和头结点竞争。
简单理解:公平是按顺序加锁,非公平是不保证按顺序加锁(其实是外部线程和队列中线程竞争),处于阻塞状态的线程必须依赖别的线程释放锁,才能被唤醒去获取锁。
参考:cloud.tencent.com/developer/a…
18,AbstractQueuedSynchronizer为何使用双向队列? aqs为何使用双向队列(即双向链表)的缘由,由于新进入阻塞状态的线程要存入尾部节点,头结点保存了尾部节点指针,这样避免了每次尾插法都要顺序遍历一次,直接根据头结点中的尾指针就能够插入了,提升了入队效率。 在移除头结点时,下一个节点升级为head节点时能快速与尾节点关联起来。
19,读写锁,ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁 ReentrantReadWriteLock 的核心是由一个基于AQS的同步器 Sync 构成,而后由其扩展出 ReadLock (共享锁), WriteLock (排它锁)所组成 线程进入读锁的前提条件: 没有其余线程的写锁, 没有写请求或者有写请求,但调用线程和持有锁的线程是同一个
线程进入写锁的前提条件:
没有其余线程的读锁
没有其余线程的写锁
复制代码
20,读写锁的使用场景:读多写少,使用此类锁同步机制则能够提升并发量(www.jianshu.com/p/9f98299a1…
21,锁降级,指的是写锁降级为读锁,实际是持有一个写锁没释放,再去申请一个读锁,再释放写锁,保留读锁,使用场景:若是当前线程不获取读锁而直接释放写锁,假设此刻另外一个线程(T)获取了写锁并修改了数据,那么当前线程是没法感知线程T的数据更新,ReentrantReadWriteLock不支持锁升级
20,为啥覆盖equals 时要重写hashcode?如何重写? 举个例子,若是重写equals方法,让对象相等,可是若是不重写hashcode,会致使使用Map结构存储数据时,会致使相等对象存储多个,也就是分布在多个hash槽 重写参考:相同属性组成相同的hashcode
4,mq的好处:广播式解耦合,异步化处理一下长耗时逻辑,流量削峰(上下游推送的流量很大)。
5,spring mvc一次请求经历了什么(SpringMVC核心处理流程)
DispatcherServlet前端控制器接收发过来的请求,交给HandlerMapping处理器映射器
HandlerMapping处理器映射器,根据请求路径找到相应的HandlerAdapter处理器适配器(处理器适配器就是那些拦截器或Controller)
HandlerAdapter处理器适配器,处理一些功能请求,也就是真正的执行业务逻辑,返回一个ModelAndView对象(包括模型数据、逻辑视图名)
ViewResolver视图解析器,先根据ModelAndView中设置的View解析具体视图
而后再将Model模型中的数据渲染到View上
这些过程都是以DispatcherServlet为中轴线进行的。
getHandler(HandlerMapping),获取页面处理器,通俗点就是获取由哪一个Controller来执行,包含方法信息以及方法参数等信息。 getHandlerAdapter(HandlerAdapter),获取HandlerAdapter,它包含一个handle方法,负责调用真实的页面处理器进行请求处理并返回一个ModelAndView。HandlerAdpter里面有一些常见的处理,好比消息转移,参数处理等,详见此图:里面的argumentResolvers能够用来处理请求的参数,messageConverts是做消息转换等等
3,Struts和spring mvc区别。 1、拦截机制的不一样 Struts2是类级别的拦截,每次请求就会建立一个Action,和Spring整合时Struts2的ActionBean注入做用域是原型模式prototype,而后经过setter,getter吧request数据注入到属性。Struts2中,一个Action对应一个request,response上下文,在接收参数时,能够经过属性接收,这说明属性参数是让多个方法共享的。Struts2中Action的一个方法能够对应一个url,而其类属性却被全部方法共享,这也就没法用注解或其余方式标识其所属方法了,只能设计为多例。 SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,因此方法直接基本上是独立的,独享request,response数据。而每一个方法同时又何一个url对应,参数的传递是直接注入到方法中的,是方法所独有的。处理结果经过ModeMap返回给框架。在Spring整合时,SpringMVC的Controller Bean默认单例模式Singleton,因此默认对全部的请求,只会建立一个Controller,有应为没有共享的属性,因此是线程安全的,若是要改变默认的做用域,须要添加@Scope注解修改。 Struts2有本身的拦截Interceptor机制,SpringMVC这是用的是独立的Aop方式,这样致使Struts2的配置文件量仍是比SpringMVC大。 2、底层框架的不一样 Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC(DispatcherServlet)则采用Servlet实现。Filter在容器启动以后即初始化;服务中止之后销毁,晚于Servlet。Servlet在是在调用时初始化,先于Filter调用,服务中止后销毁。 3、性能方面 Struts2是类级别的拦截,每次请求对应实例一个新的Action,须要加载全部的属性值注入,SpringMVC实现了零配置,因为SpringMVC基于方法的拦截(更加轻量),有加载一次单例模式bean注入。因此,SpringMVC开发效率和性能高于Struts2。
4,StackOverflowError和OutofMemoryError如何发生,怎么模拟(StackOverflowError栈溢出,如方法的递归调用,OutofMemoryError内存耗尽,好比不断建立线程分配内存) 5,jvm已经发展处三种比较成熟的垃圾收集算法:1.标记-清除算法;2.复制算法;3.标记-整理算法(标记-压缩法);4.分代收集算法,参考:www.cnblogs.com/nantang/p/5… 6,Jvm启动参数 通常用到最多的是 -Xms512m 设置JVM促使内存为512m。此值能够设置与-Xmx相同,以免每次垃圾回收完成后JVM从新分配内存。 -Xmx512m ,设置JVM最大可用内存为512M。 -Xmn200m:设置年轻代大小为200M。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代通常固定大小为64m,因此增大年轻代后,将会减少年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8(young占30%左右) 7,gc,垃圾回收算法,经常使用的是分代收集算法(新生代和老年代分开处理),分代收集算法是复制算法和标记清除法的两者整合 8,full gc Full GC 若是某个(些)对象(原来在内存中存活的对象或者新建立的对象)因为以上缘由须要被移动到老年代中,而老年代中没有足够空间容纳这个(些)对象,那么会触发一次Full GC,Full GC会对整个Heap进行一次GC,若是Full GC后还有没法给新建立的对象分配内存,或者没法移动那些须要进入老年代中的对象,那么JVM抛出OutOfMemoryError
简单理解gc 对象在Eden Space建立,当Eden Space满了的时候,gc就把全部在Eden Space中的对象扫描一次,把全部有效的对象复制到第一个Survivor Space,同时把无效的对象所占用的空间释放。当Eden Space再次变满了的时候,就启动移动程序把Eden Space中有效的对象复制到第二个Survivor Space,同时,也将第一个Survivor Space中的有效对象复制到第二个Survivor Space。若是填充到第二个Survivor Space中的有效对象被第一个Survivor Space或Eden Space中的对象引用,那么这些对象就是长期存在的,此时这些对象将被复制到Permanent Generation。若垃圾收集器依据这种小幅度的调整收集不能腾出足够的空间,就会运行Full GC,此时JVM GC中止全部在堆中运行的线程并执行清除动做。
绝大多数刚建立的对象会被分配在Eden区,其中的大多数对象很快就会消亡。Eden区是连续的内存空间,所以在其上分配内存极快; 最初一次,当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0(此时,Survivor1是空白的,两个Survivor总有一个是空白的); 下次Eden区满了,再执行一次Minor GC,将消亡的对象清理掉,将存活的对象复制到Survivor1中,而后清空Eden区; 将Survivor0中消亡的对象清理掉,将其中能够晋级的对象晋级到Old区,将存活的对象也复制到Survivor1区,而后清空Survivor0区; 当两个存活区切换了几回(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制,大于该值进入老年代,但这只是个最大值,并不表明必定是这个值)以后,仍然存活的对象(其实只有一小部分,好比,咱们本身定义的对象),将被复制到老年代 参考:www.cnblogs.com/bonelee/p/8…
9,cms: PS MarkSweep:老年代收集器,是一个能够并行标记和清理垃圾的回收器,无整理,使用的是空闲列表的方式,就像一个多线程版本的Serial Old收集器 能作到老年代提早GC的垃圾回收器有CMS收集器,但它的搭配伙伴是ParNew,由ParNew来执行新生代垃圾回收。
CMS(Concurrent Mark Sweep)收集器:老年代收集器,致力于获取最短回收停顿时间(即缩短垃圾回收的时间),使用标记清除算法,多线程,优势是并发收集(用户线程能够和GC线程同时工做),停顿小。使用-XX:+UseConcMarkSweepGC进行ParNew+CMS+Serial Old进行内存回收,优先使用ParNew+CMS(缘由见后面),当用户线程内存不足时,采用备用方案Serial Old收集
www.cnblogs.com/zhguang/p/3… www.iteye.com/topic/11194…
10,事务是必须知足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性:一个事务(transaction)中的全部操做,要么所有完成,要么所有不完成,不会结束在中间某个环节。事务在执行过程当中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务历来没有执行过同样。 一致性:在事务开始以前和事务结束之后,数据库的完整性没有被破坏。这表示写入的资料必须彻底符合全部的预设规则,这包含资料的精确度、串联性以及后续数据库能够自发性地完成预约的工做。 隔离性:数据库容许多个并发事务同时对其数据进行读写和修改的能力,隔离性能够防止多个事务并发执行时因为交叉执行而致使数据的不一致。事务隔离分为不一样级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。 持久性:事务处理结束后,对数据的修改就是永久的,即使系统故障也不会丢失
11,java中的sleep()和wait()的区别 sleep()方法致使了程序暂停执行指定的时间,让出cpu该其余线程,可是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。 在调用sleep()方法的过程当中,线程不会释放对象锁。 而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
12,重排序 编译期重排序的典型就是经过调整指令顺序,在不改变程序语义的前提下,尽量减小寄存器的读取、存储次数,充分复用寄存器的存储值。 13,happens-before原则规则: 程序次序规则:一个线程内,按照代码顺序,书写在前面的操做先行发生于书写在后面的操做(有依赖关系的逻辑执行前后顺序是明确知道的); 锁定规则:一个unLock操做先行发生于后面对同一个锁额lock操做; volatile变量规则:对一个变量的写操做先行发生于后面对这个变量的读操做; 传递规则:若是操做A先行发生于操做B,而操做B又先行发生于操做C,则能够得出操做A先行发生于操做C; 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个一个动做; 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生; 线程终结规则:线程中全部的操做都先行发生于线程的终止检测,咱们能够经过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行; 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始 14,jmm: Java内存模型是围绕着并发编程中原子性、可见性、有序性这三个特征来创建的,www.cnblogs.com/lewis0077/p… 堆,方法区,本地方法区,方法栈,程序计数器。
15,Java中notify和notifyAll的区别 Java object提供了两个方法notify和notifyAll来唤醒在某些条件下等待的线程,你可使用它们中的任何一个,可是Java中的notify和notifyAll之间存在细微差异,这使得它成为Java中流行的多线程面试问题之一。当你调用notify时,只有一个等待线程会被唤醒并且它不能保证哪一个线程会被唤醒,这取决于线程调度器。虽然若是你调用notifyAll方法,那么等待该锁的全部线程都会被唤醒,可是在执行剩余的代码以前,全部被唤醒的线程都将争夺锁定,这就是为何在循环上调用wait,由于若是多个线程被唤醒,那么线程是将得到锁定将首先执行,它可能会重置等待条件,这将迫使后续线程等待。所以,notify和notifyAll之间的关键区别在于notify()只会唤醒一个线程,而notifyAll方法将唤醒全部线程。
wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于:wait()方法当即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。
备注:condition的signal方法唤醒队列头部的。
16,线程共包括如下5种状态。
17,synchronize保证了同步代码块内的共享变量可见性,volatile保证声明的共享变量可见性 当一个变量定义为 volatile 以后,将具有两种特性: 1.保证此变量对全部的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能当即同步到主内存,以及每次使用前当即从主内存刷新。但普通变量作不到这点,普通变量的值在线程间传递均须要经过主内存(详见:Java内存模型)来完成。
2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操做,这个操做至关于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障以前的位置),只有一个CPU访问内存时,并不须要内存屏障;(什么是指令重排序:是指CPU采用了容许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。
Volatile底层实现:Lock前缀指令致使在执行指令期间,声言处理器的 LOCK# 信号。在多处理器环境中,LOCK# 信号确保在声言该信号期间,处理器能够独占使用任何共享内存。(由于它会锁住总线,致使其余CPU不能访问总线,不能访问总线就意味着不能访问系统内存),可是在最近的处理器里,LOCK#信号通常不锁总线,而是锁缓存,毕竟锁总线开销比较大
18,synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(二者皆可), synchronized是java关键字,lock(reentrantlock)是基于cas乐观锁机制实现。 1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现; 2)synchronized在发生异常时,会自动释放线程占有的锁,所以不会致使死锁现象发生;而Lock在发生异常时,若是没有主动经过unLock()去释放锁,则极可能形成死锁现象,所以使用Lock时须要在finally块中释放锁; 3)Lock可让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不可以响应中断; 4)经过Lock能够知道有没有成功获取锁,而synchronized却没法办到。 5)Lock能够提升多个线程进行读操做的效率。 6) Lock能够调用await方法让出锁资源,同时能够调用notify通知其它线程从新获取锁资源,这是synchronized不具有的 在性能上来讲,若是竞争资源不激烈,二者的性能是差很少的,而当竞争资源很是激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。因此说,在具体使用时要根据适当状况选择
19,公平锁指的是线程获取锁的顺序是按照加锁顺序来的,而非公平锁指的是抢锁机制,先lock的线程不必定先得到锁。 NonfairSync和FairSync主要就是在获取锁的方式上不一样,公平锁是按顺序去获取,而非公平锁是抢占式的获取,lock的时候先去尝试修改state变量,若是抢占成功,则获取到锁。 reentrantlock的实现基于AQS(AbstractQueuedSynchronizer)实现,内部经过自旋的方式完成锁的调度,锁的实现是基于cas(compareAndSet),参考:www.jianshu.com/p/fadac70b2…
20,ReentrantLock.lockInterruptibly容许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException
lock 与 lockInterruptibly比较区别在于: lock 优先考虑获取锁,待获取锁成功后,才响应中断。(此线程在运行中, 不会收到提醒,可是此线程的 “打扰标志”会被设置, 能够经过isInterrupted()查看并做出处理) lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取。
可重入特性是在递归调用场景下,防止被调用过程阻塞 21,自定义锁:blog.csdn.net/u012545728/… 22,java中的基本数据类型必定存储在栈中吗?,这句话确定是错误的。 基本数据类型是放在栈中仍是放在堆中,这取决于基本类型在何处声明,下面对数据类型在内存中的存储问题来解释一下: 一:在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法创建一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的缘由 在方法中声明的变量能够是基本类型的变量,也能够是引用类型的变量。 (1)当声明是基本类型的变量的时,其变量名及值(变量名及值是两个概念)是放在JAVA虚拟机栈中 (2)当声明的是引用变量时,所声明的变量(该变量其实是在方法中存储的是内存地址值)是放在JAVA虚拟机的栈中,该变量所指向的对象是放在堆类存中的。 二:在类中声明的变量是成员变量,也叫全局变量,放在堆中的(由于全局变量不会随着某个方法执行结束而销毁)。 一样在类中声明的变量便可是基本类型的变量 也但是引用类型的变量 (1)当声明的是基本类型的变量其变量名及其值放在堆内存中的 (2)引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。引用变量名和对应的对象仍然存储在相应的堆中
1,为何wait()方法和notify()/notifyAll()方法要在同步块中被调用 这是JDK强制的,wait()方法和notify()/notifyAll()方法在调用前都必须先得到对象的锁
2,wait()方法和notify()/notifyAll()方法在放弃对象监视器时有什么区别 wait()方法当即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器
23,java中用到的线程调度算法是什么 抢占式。一个线程用完CPU以后,操做系统会根据线程优先级、线程饥饿状况等数据算出一个总的优先级并分配下一个时间片给某个线程执行
24,java异常
Throwable:有两个重要的子类:Exception(异常)和Error(错误),二者都包含了大量的异常处理类。
一、Error(错误):是程序中没法处理的错误,表示运行应用程序中出现了严重的错误。此类错误通常表示代码运行时JVM出现问题。一般有Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。好比说当jvm耗完可用内存时,将出现OutOfMemoryError。此类错误发生时,JVM将终止线程。
这些错误是不可查的,非代码性错误。所以,当此类错误发生时,应用不该该去处理此类错误。
二、Exception(异常):程序自己能够捕获而且能够处理的异常。 Exception这种异常又分为两类:运行时异常和编译异常。
一、运行时异常(不受检异常,uncheck):RuntimeException类极其子类表示JVM在运行期间可能出现的错误。好比说试图使用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)。此类异常属于不可查异常,通常是由程序逻辑错误引发的,在程序中能够选择捕获处理,也能够不处理。
二、编译异常(受检异常,check):Exception中除RuntimeException极其子类以外的异常。若是程序中出现此类异常,好比说IOException,必须对该异常进行处理,不然编译不经过。在程序中,一般不会自定义该类异常,而是直接使用系统提供的异常类。
25,hashMap:threshold(进行扩容时所须要的判断基础,初始化为16),loadfactor是0.75,每次扩容是按照2倍扩容,扩容后threshold=table.length* loadfactor www.cnblogs.com/chengxiao/p…
26,ConcurrentHashMap则采用了不一样的线程安全保证方式——分段锁。它不像Hashtable那样将整个table锁住而是将数组元素分段加锁,若是线程1访问的元素在分段segment1,而线程2访问的元素在分段segment2,则它们互不影响能够同时进行操做。如何合理的进行分段就是其关键问题 a, ConcurrentHashMap在数据查找的时候,为何要两次hash?第一次hash是肯定segement的位置,第二次hash是肯定segement中链表的位置。 b,ConcurrentHashMap扩容,只扩容segement中的数组大小。
27,自旋锁便是某一线程去尝试获取某个锁时,若是该锁已经被其余线程占用的话,此线程将不断循环检查该锁是否被释放,而不是让此线程挂起或睡眠。它属于为了保证共享资源而提出的一种锁机制,与互斥锁相似,保证了公共资源在任意时刻最多只能由一条线程获取使用,不一样的是互斥锁在获取锁失败后将进入睡眠或阻塞状态
28,Comparable和Comparator区别比较 Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器,咱们若须要控制某个类的次序,能够创建一个“该类的比较器”来进行排序。 Comparable至关于“内部比较器”,而Comparator至关于“外部比较器”。 两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个能够比较的对象,可是须要修改源代码。 用Comparator 的好处是不须要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象须要做比较的时候,把比较器和对象一块儿传递过去就能够比大小了, 而且在Comparator 里面用户能够本身实现复杂的能够通用的逻辑,使其能够匹配一些比较简单的对象,那样就能够节省不少重复劳动了。 29,若是设置线程池的大小,目前业务都是io密集型的,耗时在io,所以线程池能够设置大一些,接受更多的网络请求,常见设置是99.9线耗时(秒)*qps,便是每一个线程每秒处理的请求
30,减小fullgc次数,原理上把大对象移到堆外,减小对堆空间的占用,堆空间满的时候才会触发fullgc,只有堆空间被写满的次数少了,才能减小fullgc 31,java全部的gc都会stop-the-world,包括young gc和old gc。 32,参考:www.cnblogs.com/yang-hao/p/… Minor GC触发条件 一、eden区满时,触发MinorGC。即申请一个对象时,发现eden区不够用,则触发一次MinorGC 注:新生代分为三个区域,eden space, from space, to space。默认比例是8:1:1。在MinorGC时,会把存活的对象复制到to space区域,若是to space区域不够,则利用担保机制进入老年代区域。 对eden space, from space, to space的理解:每次分配eden space空间,若是不够,则小于 to space大小的对象复制到 to space,而后to space和from space换位置,因此咱们看到的to space一直是空的。
Full GC触发条件 老生代空间不够分配新的内存(old区不足以存放从young区复制过来的对象)
33,EHCache(Terrcotta BigMemory)的 off-heap(堆外内存,操做系统层面的堆外内存,不受gc影响)将你的对象从堆中脱离出来序列化,而后存储在一大块内存中,这就像它存储到磁盘上上同样,但它仍然在RAM中 34,Java缓存类型 2.1 堆内缓存 使用Java堆内存来存储对象。可使用Guava Cache、Ehcache、MapDB实现。 优势:使用堆缓存的好处是没有序列化/反序列化,是最快的缓存; 缺点:很明显,当缓存的数据量很大时, GC暂停时间会变长,存储容量受限于堆空间大小;通常经过软引用/弱引用来存储缓存对象,即当堆内存不足时,能够强制回收这部份内存释放堆内存空间。通常使用堆缓存存储较热的数据。 2.2 堆外缓存 即缓存数据存储在堆外内存。可使用Ehcache 3.x、MapDB实现。
优势:能够减小GC暂停时间(堆对象转移到堆外,GC扫描和移动的对象变少了),能够支持更大的缓存空间(只受机器内存大小限制,不受堆空间的影响)。 缺点:读取数据时须要序列化/反序列化,会比堆缓存慢不少
34,java的本地缓存类型 1,分为堆内和堆内两种类型的缓存数据,常见的堆内缓存:如hashMap,或者guavacache,它们都收到jvm gc的影响。堆外内存有两种类型:受jvm gc影响的堆外缓存和操做系统层面的堆外缓存,受gc影响的堆外内存能够用过nio的DirectByteBuffer申请内存空间,不受gc影响的堆外内存能够经过ehcache(堆外,堆内,文件模式都支持)管理 2,DirectByteBuffer(调用unsafe的native方法申请分配内存)申请的内存空间是堆外内存,这块内存的地址会被Cleaner持有(ByteBuffer.allocateDirect,分配内存时,将这块的内存地址给Cleaner),在gc的时候,若是这块内存空间出现无引用以后,就会被释放,也就是说这块内存空间是受到gc影响的
Cleaner类继承自PhantomReference< Object>在此处保留Cleaner对象的虚引用。此类中还包含一个静态DirectByteBuffer引用队列用于得知那些虚引用所指向的对象已回收,这是一个很棒的设计由于jvm不知道堆外内存的使用状况,经过DirectByteBuffer对象的回收来间接控制堆外内存的回收。 参考:blog.csdn.net/Big_Blogger…
35,类加载器(www.importnew.com/6581.html,h… 类加载机制,简单理解就是委托、可见性和单一性。
<1>Bootstrap类加载器负责加载rt.jar中的JDK类文件,它是全部类加载器的父加载器
<2>Extension将加载类的请求先委托给它的父加载器,也就是Bootstrap,若是没有成功加载的话,再从jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下加载类。Extension加载器由sun.misc.Launcher$ExtClassLoader实现。这为引入除Java核心类之外的新功能提供了一个标准机制
<3>System类加载器(又叫做Application类加载器),它负责从classpath环境变量中加载某些应用相关的类,Application类加载器是Extension类加载器的子加载器。经过sun.misc.Launcher$AppClassLoader实现 <4>自定义加载器,MyClassLoader extends ClassLoader,通常只须要重写findClass(从别的地方获取类文件),最好不要重写loadClass方法,由于这样容易破坏双亲委托模式。
Java类加载器的做用就是在运行时加载类。Java类加载器基于三个机制:委托、可见性和单一性。委托机制是指将加载一个类的请求交给父类加载器,若是这个父类加载器不可以找到或者加载这个类,那么再加载它。可见性的原理是子类的加载器能够看见全部的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。单一性原理是指仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。正确理解类加载器可以帮你解决NoClassDefFoundError和java.lang.ClassNotFoundException,由于它们和类的加载相关。类加载器一般也是比较高级的Java面试中的重要考题,Java类加载器和工做原理以及classpath如何运做的常常被问到。Java面试题中也常常出现“一个类是否能被两个不一样类加载器加载”这样的问题。这篇教程中,咱们将学到类加载器是什么,它的工做原理以及一些关于类加载器的知识点。
36, (1)阿里的面试官问我,能够不能够本身写个String类
答案:不能够,由于 根据类加载的双亲委派机制,会去加载父类,父类发现冲突了String就再也不加载了;
(2)可否在加载类的时候,对类的字节码进行修改
答案:能够,使用Java探针技术,能够参考:Java探针-Java Agent技术-阿里面试题
(3)如何实现热部署:自定义classLoader就能够了,热部署以前,销毁(即gc回收掉)以前部署的classLoader
37,class什么时候触发初始化
为一个类型建立一个新的对象实例时(好比new、反射、序列化) 调用一个类型的静态方法时(即在字节码中执行invokestatic指令) 调用一个类型或接口的静态字段,或者对这些静态字段执行赋值操做时(即在字节码中,执行getstatic或者putstatic指令),不过用final修饰的静态字段除外,它被初始化为一个编译时常量表达式 调用JavaAPI中的反射方法时(好比调用java.lang.Class中的方法,或者java.lang.reflect包中其余类的方法) 初始化一个类的派生类时(Java虚拟机规范明确要求初始化一个类时,它的超类必须提早完成初始化操做,接口例外) JVM启动包含main方法的启动类时。
38,数据库链接池简单实现,参考:blog.csdn.net/moakun/arti… public class SimplePoolDemo { //建立一个链接池 private static LinkedList pool = new LinkedList();
//初始化10个链接
static{
try {
for (int i = 0; i < 10; i++) {
Connection conn = DBUtils.getConnection();//获得一个链接
pool.add(conn);
}
} catch (Exception e) {
throw new ExceptionInInitializerError("数据库链接失败,请检查配置");
}
}
//从池中获取一个链接
public static Connection getConnectionFromPool(){
return pool.removeFirst();//移除一个链接对象
}
//释放资源
public static void release(Connection conn){
pool.addLast(conn);
}
复制代码
}
C3p0,dbcp,druid的区别: c3p0有自动回收空闲链接功能,dbcp没有自动的去回收空闲链接的功能
C3P0提供最大空闲时间,DBCP提供最大连数。 Druid具有的功能更加丰富,还具有sql注入的语法校验。 参考:mp.weixin.qq.com/s?__biz=MzI…
Dbcp源码解读:www.jianshu.com/p/f430c1d13… Dbcp源码读后总结: dbcp有个定时器(基于定时器实现)去保证链接池维持一个称做minIdle状态(最小闲置状态,若是是无并发场景,minIdle为1就够了),大部分状况下都超过minIdle,由于数据库访问频率都很高的,当访问量增长的时候,会建立链接直至最大值为maxActive,若是链接数超过maxActive,请求会被阻塞(分为永久阻塞和限时阻塞,可配置最长等待时间maxWait)。当qps降下来,链接数不须要那么多的时候,会保持链接数在maxIdle(最大闲置数),多余的会被销毁(若是maxIdle==maxActive,就不会出现销毁了,所以生产环境通常配置maxIdle和maxActive相同)。
欢迎打赏