java常见问题概述

1、如何理解内存泄漏问题?有哪些情况会导致内存泄露?如何解决?

对于应用程序来说,当对象已经不再被使用,但是java的垃圾回收器不能回收他们的时候,就产生了内存泄漏。未引用对象将会被垃圾回收器回收,而引用对象却不会,未引用对象显然都是无用的对象,然而无用的对象并不都是未引用对象,有些无用对象也有可能是引用对象,这部分对象正是内存泄漏的来源。

如何阻止内存泄漏

使用List,Map等集合时,使用完成后赋值为null

使用大对象时,在用完后赋值为null

使用字符串处理,避免使用String,应大量使用StringBuffer,每一个String对象都得独立占用内存一块区域

尽量少用静态变量,因为静态变量存放在永久代

目前已知jdk1.6的substring()方法会导致内存泄漏

避免一些死循环等重复创建或对集合添加元素,称爆内存

简洁数据结构,少用静态集合等

及时的关闭打开的文件,socker句柄

2、怎样理解强一致性,弱一致性,最终一致性?

cap原理中,有三个要素:一致性,可用性,分区容忍性

cap原理三个要素中最多只能同时实现俩点。对于分布式数据系统,分区容错性是最基本要求,大多数情况下会牺牲一致性而换取高可用性。

最终一致性从客户端来看一致性要指的是多并发访问时更新过的数据如何获取的问题,从服务端来看,则是更新如何复制分布到整个系统,以保证数据的最终一致。对于关系形数据库,要求更新过的数据能被后续的访问都能看到,是强一致性;如果能容忍后续的部分或者全部访问不到,是弱一致性;如果经过一段时间后要求能访问到更新的数据,则是最终一致性

3、分布式锁有哪些解决方案

1、利用数据库实现方案,利用数据库自身提供的锁机制实现,要求数据库支持行级锁,实现简单稳定可靠,性能差无法适应高并发的场景,容易出现死锁的情况,无法优雅的实现阻塞式锁

2、利用redis实现,使用sexnx和lua脚本机制实现,保证对缓存操作序列的原子性,性能好,实现相对交复杂,有出现死锁的可能性,无法优雅的实现阻塞式锁

3、利用zookeeper,基于zk的节点特性及watch机制实现,性能好,稳定可靠性高,能较好的实现阻塞式锁,实现相对复杂

4、如何解决Redis缓存穿透的问题?

缓存冲突是指查询一个一定不存在的数据,由于缓存不命中,并且出于容错的考虑,请求全部都走数据库。

请求的数据在缓存中大量不命中,导致请求走数据库,缓存穿透如果发生,可能直接导致数据库搞垮,导致整个服务瘫痪

方案:

1、由于我们请求的参数是不合法的,于是我们可以使用布隆过滤器提前拦截

2、当我们从数据库中找不到的时候,我们也将这个空对象设置到缓存里面去,下次再请求,就可以从缓存里面获取 

缓存击穿:key对应的数据存在,但在redis中过期,此时如果有大量的并发请求过来,这些请求发现缓存过期一般都会从数据库中加载数,大并发的情况下可能会击垮数据库

缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间端失效,这样在失效的时候,也会给后端带来很大的压力

5、Redis hash算法用的是什么?

6、redis集群方案? 

7、乐观锁和悲观锁的原理及其应用?

乐观锁

乐观锁是一种并发控制的方法,假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据,在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务修改了该数据,如果其他事务有更新的话,正在提交的事务会进行回滚。

1、读取:事务将数据读入缓存,系统会给事务分派一个时间戳

2、校验:事务执行完毕后,进行提交,这时同步校验所有的事务,如果事务所读取得数据之后又被其他事务修改,则产生冲突,进行回滚。

3、写入:通过校验阶段后,将更新的数据写入数据库

乐观并发控制多数用于共享数据竞争不大,冲突较少的情况下,回滚事务的成本会低于读取数据加锁的成本,能够获得更高的吞吐量。

使用乐观锁的时候,会未数据增加一个版本标识,当读取数据时,将版本标识的值一同读出,数据每更新一下,同时对版本标识进行更新,当提交数据的时候,会判断当前版本号是不是该数据最新的版本号,如果不是则直接回滚,是的话正常更新。

悲观锁

 如果一个事务执行的操作读取某行数据应用了锁, 那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作

使用场景

乐观锁使用于写比较少的情况下多读场景,即冲突很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量,但如果时多写的情况下,一般会经常产生冲突,着就会导致上层应用会不断的进行retry,降低性能,一般多写的场景用悲观锁就比较适合了。

乐观锁的俩种实现方式:

版本号机制:在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加1,当线程A要更新数据值时,在读取数据的同时也会读取version的值,在提交更新时, 若刚才读取到的version值为当前数据库中的version值相等时菜更新,否则重试更新操作

CAS算法:比较与交换,不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步,其中三个操作数

需要读写的内存值V

进行比较的值A

写入的新值B

当且仅当V的值等于A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作,一般情况下,如果时一个自旋的操作,将会不断的重试

 

8、受检异常和非受检异常的区别?

异常体系分类图

Error:和底层硬件有关的错误。不是程序本身的异常,即使捕捉这种异常,我们也解决不了

Exception:程序本身所抛出的异常。

        受检异常(CheckedException):可能是由一些外部的因素所引起的,是必须捕获的异常,否则无法通过编译(如:NullPointerException,ClassCastException,ArrayIndexsOutOfBoundsException,ArithmeticException)

        非受检异常(RuntimeException):可能是由于开发的时候逻辑错误,可以捕获也可以不捕获,不捕获的话,由jvm来处理(如:FileNotFoundException,IOException,SQLException)

异常通过trc - catch 或者throws来解决

9、了解软引用,弱引用,虚引用,强引用?

 

强引用:平日里面的用到的new了一个对象就是强引用,例如 Object obj = new Object();

当JVM的内存空间不足时,宁愿抛出OutOfMemoryError使得程序异常终止也不愿意回收具有强引用的存活着的对象!记住是存活着,不可能是你new一个对象就永远不会被GC回收。

当一个普通对象没有其他引用关系,只要超过了引用的作用域或者显示的将引用赋值为null时,你的对象就表明不是存活着,这样就会可以被GC回收了。当然回收的时间是不一定的具体得看GC回收策略

软引用:软引用的生命周期比强引用短一些。软引用是通过SoftReference类实现的。这样就是一个简单的软引用使用方法。通过get()方法获取对象。当JVM认为内存空间不足时,就回去试图回收软引用指向的对象,也就是说在JVM抛出OutOfMemoryError之前,会去清理软引用对象。软引用可以与引用队列(ReferenceQueue)联合使用。

弱引用:弱引用是通过WeakReference类实现的,它的生命周期比软引用还要短,也是通过get()方法获取对象。在GC的时候,不管内存空间足不足都会回收这个对象,同样也可以配合ReferenceQueue 使用,也同样适用于内存敏感的缓存。ThreadLocal中的key就用到了弱引用

虚引用:通过PhantomReference类实现的。任何时候可能被GC回收,就像没有引用一样。无法通过虚引用访问对象的任何属性或者函数。那就要问了要它有什么用?虚引用仅仅只是提供了一种确保对象被finalize以后来做某些事情的机制。比如说这个对象被回收之后发一个系统通知啊啥的

虚引用是必须配合ReferenceQueue 使用的,具体使用方法和上面提到软引用的一样。主要用来跟踪对象被垃圾回收的活动。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章