Java常见面试题

八大基本数据类型及其默认值

Byte(1字节),short(2字节),int(4字节),long(8字节),folat(4字节),double(8字节),boolean(未规定),char(2字节)。Byte,short,int,long默认值是0,float,double默认值是0.0,Boolean默认值是true和false,char默认值是空

equals和==区别

既可以比较基本数据类型也可以比较引用数据类型,对于基本数据类型比较的就是本身的数据值,对于引用类型就是比较内存中的地址值是否相同
equals()是属于java.lang.Object类中的方法,如果该方法没有被重写过默认和
相同,但可以看到String类、Date类等类的equals()方法是被重写过的,比较的是内容是否相同

java四个访问修饰符

  • private:本类中
  • default:本包中
  • protected:不同包的子类
  • public:所有

重载和重写

重载:方法名相同,参数个数不同

重写:方法名相同,参数个数相同

重载可以修改返回值类型,重写不可以修改返回值类型

String、StringBuffer与StringBuilder之间区别

  • string不可变、线程安全
  • stringBuffer可变,效率低,线程安全
  • stringBuilder可变、效率高,线程不安全

sleep() 和 wait() 有什么区别?

sleep来自Thread类,和wait来自Object类。

sleep()可以在任何地方使用,而wait()只能在同步方法或同步代码块中使用

sleep方法不会释放锁,wait() 方法会释放锁

sleep需要捕获异常,wait不需要。

String str="i"与 String str=new String(“i”)一样吗?

不一样。主要是内存分配方式不一样。String str=“i”,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中

LinkedList、ArrayList和Vector

  • LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全。扩容就是新建节点进行指针指向即可
  • ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全。Arraylist是动态扩容机制。初始容量为10,扩容机制为1.5倍
  • Vector 接口实现类 数组, 同步, 线程安全

对HashMap的理解

(1)HashMap同Hashtable,TreeMap都是Map接口的实现类,基于哈希表进行存储,都是以键值对<key,value>的形式存储和操作数据的容器类型。

(2)HashMap数组每一个元素的初始值都是Null。允许Null键和Null值,不保证有序。

(3)HashMap的默认初始长度是16,并且每次自动扩展或是手动初始化时,长度必须是2的幂。之所以选择16,是为了服务于key映射到index的Hash算法。

(4)HashMap中有两个很重要的参数,容量(Capacity)和负载因子(Load factor)。当bucket填充的数目(即hashmap中元素的个数)大于capacity*load factor时就需要调整buckets的数目为当前的2倍。

1.实现的接口
HashMap同Hashtable,TreeMap都是Map接口的实现类,都是以键值对<key,value>的形式存储和操作数据的容器类型。

2.底层存储结构
HashMap基于哈希表进行存储,在JDK1.7之前由数组+链表组成。JDK1.8以后由数组+链表+红黑树,在解决哈希冲突时有较大的变化,当链表长度大于阈值(默认为8)并且数组长度大于64时,将链表转换成红黑树,以减少搜索时间。

3.存储方式
HashMap中的每一个元素(k,v键值对),都会被封装成一个内部类Entry对象。该对象存有key(键)、value(值)、next(下一个元素)、hash(哈希值);
在数组中保存Entry对象;
每次存储元素(Entry对象)时,先使用hash方法计算key的hashcode,再通过(长度-1)&hash得到这个元素在数组中的位置下标,如果数组该位置已经存放有元素(产生哈希冲突),继续判断key的内容是否相等,如果相等,覆盖原value值;如果不相等,判断当前节点类型是否是TreeNode<k,v>树形节点,若是树形节点,创建树形节点插入红黑树;若不是,创建普通Node<k,v>加入链表尾部。判断链表长度大于阈值(默认为8)并且数组长度大于64,如果满足,将链表转换成红黑树;如果不满足,数组扩容。
4.扩容方式:
当HashMap中的元素个数超过数组长度×加载因子时,会进行数组扩容;(数组长度:默认为16,也可通过有参构造方法自定义,一般为2的幂次方,若不是,HashMap也会转换成2的幂次方,作为数组的初始化长度;加载因子:默认为0.75,也可以在调用HashMap有参构造方法时在参数2的位置上自定义,建议不要超过0.75)
链表长度大于阈值(默认为8)并且数组长度大于64,如果满足,将链表转换成红黑树;如果不满足,数组扩容。
扩容时,调用resize( )方法,按原有数组的长度,扩容一倍(即扩容至原有长度的2倍),此时,会根据原数据键的hashcode重新计算数据的存储位置。
5.特点
非线程安全:由于HashMap内部方法未使用synchronized同步锁修饰,所以其是非线程安全的;
执行效率高:由于非线程安全的问题,自然它的执行效率要相比Hashtable高一些;
无序性:由于存储数据时,会用键的hash值与数组长度进行%模运算,计算存储在数组中的位置下标,所以是无序的。
键唯一,值可重复:允许Null key和Null value,Null Key只允许有1个,Null value 可以有多个;
6.影响因素
构建HashMap实例时有两个重要的参数会影响其性能:初始容量和加载因子
初始容量:用来规定哈希表数组的长度,默认为16,因为16是2的整数次幂,在小数据量的情况下,能减少哈希冲突,提高性能。在存储大量数据时,最好先预判数据量,按照2的幂次方,提前预设初始容量。
加载因子:用来表示哈希表中元素的填满程度,默认为0.75,越大则表示允许填满的元素就越多,哈希表的空间利用率就越高,但哈希冲突的机会增加;反之,越小则冲突机会越少,但会造成空间浪费。
所以,在设置初始容量时,应该考虑到初始容量及其加载因子,预估设置初始容量,最大限度的减少rehash重建内部数据结构操作的次数,减少扩容操作。
7.常用方法
① put(k,v) 作用:添加元素
② get(k) 作用:根据key键获取value值(试图访问一个不存在的键值对时,会引发空指针异常)
③ containskey(指定的key) 作用:判断当前集合中是否存在指定的键,返回值为boolean
④ containsvalue(指定的value) 作用:判断当前集合中是否存在指定的value值,返回值为boolean
⑤ remove(指定的key) 作用:按照指定的键删除kv键值对,返回被删除的value值
⑥ replace(指定的key,修改后的新值) 作用:按照指定的键修改kv键值对,返回修改前的value值
⑦ keySet( ) 作用:获取集合中所有的key键,返回Set类型集合
⑧ values( ) 作用: 获取集合中所有的value值,返回Collection类型集合

hashset和hashmap的区别

存储不同、放入方法不同、hashcode值不同。hashset和hashmap都是存在于java.util包中的类,用于存储数据,且都不允许集合中出现重复的元素。

一、存储不同

1、hashset:HashSet仅仅存储对象。

2、hashmap:HashMap储存键值对。

二、放入方法不同

1、hashset:hashset使用add()方法将元素放入set中。

2、hashmap:HashMap使用put()方法将元素放入map中

三、hashcode值不同

1、hashset:HashSet使用成员对象来计算hashcode值。

2、hashmap:HashMap中使用键对象来计算hashcode值

HashMap和Hashtable的区别

n 继承的父类不同。Hashtable继承自Dictionary类。而HashMap继承自AbstractMap类。但二者都实现了Map接口

n 线程安全性不同。Hashtable是线程安全的。HashMap是线程不安全的。

n 提供方法不同。HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey。Hashtable则保留了contains,containsValue和containsKey三个方法。

n key和value是否允许null值。Hashtable中,key和value都不允许出现null值。HashMap中,null可以作为key,这样的key只有一个可以有一个或多个key所对应的值为null。

n hash值不同。HashTable直接使用对象的hashCode。HashMap重新计算hash值。

初始化容量不同:HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75

扩容机制不同:当已用容量>总容量 * 负载因子时,HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 +1。

线程安全集合类与非线程安全集合类

LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;,
HashMap是非线程安全的,HashTable是线程安全的;
StringBuilder是非线程安全的,StringBuffer是线程安全的。

ConcurrentHashMap如何保证的线程安全?

JDK 1.7 时候是使用分成16个Seagment段,每个Seagment里面存储着一个HashMap和一个锁,所以说1.7能支持最大的并发量也就是16个线程

JDK1.8采用的CAS + Synchronized,每次插入的时候判断是否是第一次插入,是就通过CAS插入,然后判断f.hash是否=-1,如果是的那么其他线程在扩容,当前线程也会参与扩容;删除方法用了synchronized修饰,保障并发下删除元素的安

IntentService能用bind方式启动吗?IntentService的原理

一般是通过startService启动intentService,而不用bind,因为bind会使得IntentService跳过了onStartCommand方法。。导致任务分发出问题

在intentService的onCreate方法中开启了一个handleThread(子线程 + 初始化好looper等),同时用handler绑定该子线程的Looper。在onStartCommand中会将intent封装进Message中,发送,最后在handler的handMessage方法中,调用onHandleIntent(因此该方法是在handleThread线程操作)

Volatile关键字作用

1)volatile关键字为域变量的访问提供了一种免锁机制;

(2)使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新;

(3)因此每次使用该域就要重新计算,而不是使用寄存器中的值;

(4)volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。

synchronized关键字

synchronized是Java多线程中经常使用的一个关键字。synchronized可以保证原子性、可见性、有序性。两种用法:synchronized 方法和 synchronized 代码块。它可以用来给对象、方法或代码块进行加锁。当它锁定一个方法或者一个代码块时,同一时刻最多只有一个线程可以执行这段代码,其他线程想在此时调用该方法只能排队等候。当它锁定一个对象时,同一时刻最多只有一个线程可以对这个类进行操作,没有获得锁的线程,在该类所有对象上的任何操作都不能进行。synchronized 是悲观锁的实现synchronized锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁

悲观锁和乐观锁有什么区别

1、悲观锁,顾名思义,是悲观的,觉得不锁柱的资源会被别人的线程抢走,所以悲观锁每次获取和修改数据都会锁定数据。

典型的悲观锁案例:synchronized关键词和Lock接口。

2、乐观锁,认为自己在操作资源时不会有其他线程干扰,所以不会锁定对象,只是在更新资源时会去对比一下我修改过的数据之间是否有其他线程修改过的数据。若无修改,此次修改正常,若有其他线程修改,则放弃此次修改,并选择报错或重试。这是一个基于冲突检测的并发策略,这种并发策略的实现不需要线程挂起,因此是非阻塞同步。乐观主义锁一般采用CAS算法实现。

典型的乐观锁案例:Java并发包Atomic原子类,数据库version版本机制。

悲观锁会堵塞锁的线程,这种费用是固定的。悲观锁的原始费用高于乐观锁。虽然乐观锁的初始费用比悲观锁小,但如果一直拿不到锁或者并发量大,竞争激烈,会导致不断的重试,所以会消耗越来越多的资源,甚至超过悲观锁。

synchronized与volatile区别

1.volatile仅能使用在变量级别;
synchronized则可以使用在变量、方法、和类级别的
2.volatile仅能实现变量的修改可见性,并不能保证原子性;
synchronized则可以保证变量的修改可见性和原子性
3.volatile不会造成线程的阻塞;
synchronized可能会造成线程的阻塞。
4.volatile标记的变量不会被编译器优化;
synchronized标记的变量可以被编译器优化

抽象类和接口的区别

继承:抽象类是只能单继承的,接口是可以多实现的

属性:属性修饰符也不一样,抽象累可以是public protect 默认不写 还有final 修饰,但是接口只能是public static final修饰

方法:抽象类既可以抽象的方法,也可以具体的方法,接口只有抽象的方法,而且子类必须实现

final、finally、finalize的区别

1、final修饰符(关键字)。被final修饰的类,就意味着不能再派生出新的子类,不能作为父类而被子类继承。因此一个类不能既被abstract声明,又被final声明。将变量或方法声明为final,可以保证他们在使用的过程中不被修改。被声明为final的变量必须在声明时给出变量的初始值,而在以后的引用中只能读取。被final声明的方法也同样只能使用,即不能方法重写。2、finally是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行。try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳转到catch块中执行。finally块则是无论异常是否发生,都会执行finally块的内容,所以在代码逻辑中有需要无论发生什么都必须执行的代码,就可以放在finally块中。

3、finalize是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

多线程

线程、进程、协程的区别

进程:进程是操作系统分配系统资源和内存空间的最小单位。进程是独立的一块空间,所以资源和内存空间的切换是特别消耗资源的。

线程:线程也叫做轻量级的进程,是操作系统调用执行的最小单位。线程的资源是依赖于他的父进程,所以他的资源是共享的,线程的切换需要转换到内核态开销相对于小一些。

协程:协程是一种轻量级的线程,协程是直接在用户态就可以控制,具有对内核态来说是不可见的,所以协程的上下文切换更加的节约资源消耗

线程状态

img

① 新建状态(New Thread):在Java语言中使用new 操作符创建一个线程后,该线程仅仅是一个空对象,它具备类线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态。

线程处于创建状态时,可通过Thread类的方法来设置各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。

② 就绪状态(Runnable):使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于就绪状态。此外,如果某个线程执行了yield()方法,那么该线程会被暂时剥夺CPU资源,重新进入就绪状态。

③ 运行状态(Running):Java运行系统通过调度选中一个处于就绪状态的线程,使其占有CPU并转为运行状态。此时,系统真正执行线程的run()方法。

a) 可以通过Thread类的isAlive方法来判断线程是否处于就绪/运行状态:当线程处于就绪/运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于阻塞状态,也可能处于停止状态。

④ 阻塞和唤醒线程

阻塞状态(Blocked):一个正在运行的线程因某些原因不能继续运行时,就进入阻塞 状态。这些原因包括:

​ a)当执行了某个线程对象的sleep()等阻塞类型的方法时,该线程对象会被置入一个阻塞集内,等待超时而自动苏醒。

​ b)当多个线程试图进入某个同步区域时,没能进入该同步区域的线程会被置入锁定集,直到获得该同步区域的锁,进入就绪状态。

​ c)当线程执行了某个对象的wait()方法时,线程会被置入该对象的等待集中,知道执行了该对象的notify()方法wait()/notify()方法的执行要求线程首先获得该对象的锁。

⑤ 死亡状态(Dead):线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。

多线程创建方式

  • 继承Thread类创建线程类

  • 通过Runnable接口创建线程类

  • 通过Callable和Future创建线程

  • 使用线程池

线程池有七个参数?

corePoolSize: 线程池核心线程数最大值

maximumPoolSize: 线程池最大线程数大小

keepAliveTime: 线程池中非核心线程空闲的存活时间大小

unit: 线程空闲存活时间单位

workQueue: 存放任务的阻塞队列

threadFactory: 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。

handler: 线程池的饱和策略事件,主要有四种类型。

线程池的执行过程?

当提交一个线程执行任务时候 ,先看有没有空闲的核心线程如果有就执行,如果没有就看阻塞队列中有没有满的状态,如果没有放满则放入队列中,否则就直接看线程数量是否大于非核心线程数量,如果没有就直接创建一个非核心线程,否则就是按照指定的拒绝策略给处理。。如果一个非核心线程在某个一段时间类是空闲的那么线程池就会把这个线程自动销毁掉。

四种拒绝策略

  • AbortPolicy(抛出一个异常,默认的)
  • DiscardPolicy(直接丢弃任务)
  • DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
  • CallerRunsPolicy(交给线程池调用所在的线程进行处理)

五种阻塞队列

ArrayBlockingQueue(有界队列)是一个用数组实现的有界阻塞队列,按FIFO排序量。
LinkedBlockingQueue(可设置容量队列)基于链表结构的阻塞队列,按FIFO排序任务,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool线程池使用了这个队列
DelayQueue(延迟队列)是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。newScheduledThreadPool线程池使用了这个队列。
PriorityBlockingQueue(优先级队列)是具有优先级的无界阻塞队列;
SynchronousQueue(同步队列)一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene,newCachedThreadPool线程池使用了这个队列。

终止线程的三种方法

① 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,推荐使用。

② 使用stop方法强制终止线程(这个方法不推荐使用,因为stop和suppend、resume一样,也可能发生不可预料的结果)。

③ 使用interrupt方法中断线程

JVM

Java中四大引用类型?

强引用 :发生GC时候不会被回收

软引用:发生内存满(内存溢出的时候)会被回收(通常用于缓存)

弱引用:无论内存是否充足,都会回收。在java中,用java.lang.ref.WeakReference类来表示。

虚引用:任何时候都可能被垃圾回收器回收。如果一个对象与虚引用关联,则跟没有引用与之关联一样。虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示

线程安全集合类与非线程安全集合类

LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;,
HashMap是非线程安全的,HashTable是线程安全的;
StringBuilder是非线程安全的,StringBuffer是线程安全的。

JVM类加载过程

主要分为 加载 -> 链接 - > 初始化 三步骤

JVM的内存模型

JVM内存空间分为五部分,分别是:方法区、堆、Java虚拟机栈、本地方法栈、程序计数器

方法区主要用来存放类信息、类的静态变量、常量、运行时常量池等,方法区的大小是可以动态扩展的,

堆主要存放的是数组、类的实例对象、字符串常量池等。

Java虚拟机栈是当JVM在执行方法的各种信息,比如返回值,局部变量表和各种对象引用等,方法开始执行前就先创建栈帧入栈,执行完后就出栈。

本地方法栈提供给Native方法用的。

最后是程序计数器,程序计数器是一个比较小的内存空间,用来记录当前线程正在执行的那一条字节码指令的地址。

死锁产生的4个必要条件

互斥条件:进程要求对所匹配的资源进行排他性控制,即是一段时间内某资源仅为一进程所占有

请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放

不剥夺条件:进程已获得的资源在未使用完之前不,不能剥夺,只能在使用完时有自己释放

环路等待条件:在发生死锁时,必须存在一个进程–资源得环星链

预防死锁

资源一次性得分配:一次性分配所有得资源,这样就不会有请求了(破坏请求条件);只要一个资源得不到分配,也不给这个进程分配其他得资源(破坏请求保持条件)

可剥夺条件:即当某个进程获得部分资源,得不到其他得资源,则释放已有得资源(破坏补课剥夺得条件)

资源有序分配法:系统分配资源编号,每一个进程按编号递增的顺序请求资源,释放则反之(环路破坏)

解除死锁

**剥夺资源:**从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;

**撤消进程:**可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等

保证并发安全的三大特性?

原子性:一次或多次操作在执行期间不被其他线程影响

可见性:当一个线程在工作内存修改了变量,其他线程能立刻知道**(利用内存原子操作解决或者内存屏障或者lock前缀)**

有序性:JVM对指令的优化会让指令执行顺序改变,有序性是禁止指令重排**(内存屏障)**

什么是内存泄漏?

就是不再使用的对象或者变量一直占用着内存中,而且GC也无法去回收。这种情况是因为长生命周期的对象持有短生命周期的对象的引用就会发生内存泄漏情况。

什么情况下会内存溢出?

堆内存溢出:(1)当对象一直创建而不被回收时(2)加载的类越来越多时(3)虚拟机栈的线程越来越多时

栈溢出:方法调用次数过多,一般是递归不当造成

简述一下Java的垃圾回收机制?

在Java中,程序员不需要去指定的释放对象。而是有JVM虚拟机自行的执行,有一个垃圾回收线程(守护线程)他的优先级比较低,他不会去主动的去执行,只有虚拟机空闲或者当前的堆空间满的时候,才会去触发,去扫描回收掉没有用的对象。

当创建一个对象的时候GC就会进行监控这个对象的地址、大小以及使用情况 。GC通过有向图的方式记录管理堆,通过这种方式来判断那些可达,哪些不可达,从而来判定是否回收。

程序员可以手动的进行System.gc()来通知gc运行,但是GC并不能保证GC一定会执行

如何判断是否能够回收?

两种方式:

引用计数器法:指的是如果某个地方引用了这个对象就+1,如果失效了就-1,当为0就会回收。无法判定相互循环引用
可达性分析算法:当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。

垃圾回收算法有哪几种?

标记清除: 标记需要清除的对象,对标记的对象进行清楚操作。缺点:产生了大量的不连续碎片化对象,不利于分配新的对象。

标记整理:标记需要清除的对象,对标记的对象进行清楚操作过程中,也将没有清除的对象进行整理到内存的一端。优点:解决了标记-清理算法存在的内存碎片问题 缺点:需要局部的移动对象,降低的效率

复制算法:将内存空间分称等大的两部分,每次遍历一个区域将存货的对象放入另一个区域,然后清除掉掉这个区域所有的对象,以此类推循环。 优点:效率高速度快,也没有内存碎片的问题 缺点:堆内存大小要求高

分代算法:就是把内存空间分配成不同的年轻代、老年代和永久代,根据不同的年代使用不同的算法。

比如说 年轻代空间大而且垃圾回收发生频繁 所以采用复制算法,老年代就采用标记整理算法

垃圾收集器

Serial收集器:色而瑞
新生代采用复制算法,老年代采用标记-整理算法。单线程,所以在发生GC时候需要暂停所有线程的工作。

Parallel Scavenge收集器:帕尔雷尔
新生代采用复制算法,老年代采用标记-整理算法。多线程,Parallel Scavenge收集器关注点是**吞吐量(**高效率的 利用CPU)

ParNew收集器:帕尔new(jdk8默认设置的垃圾收集器)
新生代采用复制算法,老年代采用标记-整理算法。和Parallel 相似主要用于配合CMS收集器使用。

CMS收集器:
CMS收集器是一种 “标记-清除”算法实现的。

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户 体验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用 户线程(基本上)同时工作。

初始标记: 暂停所有的其他线程(STW),并记录下gc roots直接能引用的对象,速度很快。
并发标记: 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但
是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。
重新标记:重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对
象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法(见下面详解)做重新标记。
并发清理:开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑
色不做任何处理(见下面三色标记算法详解)。
并发重置:重置本次GC过程中的标记数据。

说一说如何理解双亲委派模型

类加载器主要有:

引导类加载器(BootstrapClassloader):负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如
rt.jar、charsets.jar等
扩展类加载器(ExtceptClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR
类包
应用程序类加载器(ApplicationClssloader):负责加载ClassPath路径下的类包,主要就是加载你自己写的那
些类
双亲委派模型就是子加载器向父级加载器向上委托它加载,如果父加载器能加载那么子加载器就不用加载直接返回,如果不能加载就有子加载器进行加载。

**如何打破双亲委派模型:**重写loadClass(String, boolean)方法

什么是CAS锁

CAS锁可以保证原子性,思想是更新内存是会判断其内存的值是否被修改了,如果没有被修改就直接更新,如果被修改了,就得重新去获取值,知道更新为止。

Synchronized锁原理和优化

在JDK1.6以后Synchronized引入了偏向锁、轻量级锁、重量级锁、锁的粗化、锁消除的优化。并发性能基本和Lock持平的。

偏向锁:是不存竞争情况下,从而在后续的代码块中没有加锁解锁的开销
轻量级锁:轻量级锁所适应的场景是线程交替执行同步块的场合
重量级锁:重量级锁首先会经过一定次数的CAS自旋操作获取锁,如果获取失败,存在同一时间多个线程访问同一把锁的场合,就会导致轻量级锁膨胀为重量级锁。是在竞争激烈的情况下创建一个monitor对象,并且将线程挂起,而挂起就要切换到内核状态执行。从而开销非常大。

UML 是什么?常用的几种图?

答:UML 是标准建模语言;常用图包括:用例图,静态图(包括类图、对象图和包图),行为图,交互图(顺序图,合作图),实现图。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是酷酷呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值