1、HashMap和ConcurrentHashMap的区别
①
HashMap
不是线程安全的,而ConcurrentHashMap
是线程安全的。②
ConcurrentHashMap
采用锁分段技术,将整个Hash
桶进行了分段segment
,也就是将这个大的数组分成了几个小的片段segment
,而且每个小的片段segment
上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段segment
,然后再在这个片段上面进行插入,而且这里还需要获取segment
锁。③
ConcurrentHashMap
让锁的粒度更精细一些,并发性能更好。
2、HashTable和ConcurrentHashMap的区别
它们都可以用于多线程的环境,但是当
Hashtable
的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。因为ConcurrentHashMap
引入了分割(segmentation
),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map
。简而言之,在迭代的过程中,ConcurrentHashMap
仅仅锁定map
的某个部分,而Hashtable
则会锁定整个map
。
3、String,StringBuffer和StringBuilder的区别
1、运行速度,或者说是执行速度,在这方面运行速度快慢为:
StringBuilder > StringBuffer > String
。
2、线程安全上,StringBuilder
是线程不安全的,而StringBuffer
是线程安全的。适用场景分析:
String
:适用于少量的字符串操作的情况StringBuilder
:适用于单线程下在字符缓冲区进行大量操作的情况StringBuffer
:适用多线程下在字符缓冲区进行大量操作的情况
4、Java内存模型:
Java虚拟机规范中将Java运行时数据分为六种。
内存模型 | 描述 |
---|---|
程序计数器 |
是一个数据结构,用于保存当前正常执行的程序的内存地址。Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,互不影响,该区域为“线程私有”。 |
Java虚拟机栈 |
线程私有的,与线程生命周期相同,用于存储局部变量表,操作栈,方法返回值。局部变量表放着基本数据类型,还有对象的引用。 |
本地方法栈 |
跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。 |
Java堆 |
所有线程共享的一块内存区域,对象实例几乎都在这分配内存。 |
方法区 |
各个线程共享的区域,储存虚拟机加载的类信息,常量,静态变量,编译后的代码。 |
运行时常量池 |
代表运行时每个class文件中的常量表。包括几种常量:编译时的数字常量、方法或者域的引用。 |
5、 类加载器工作机制:
装载
:将Java二进制代码导入jvm中,生成Class文件。连接
:a)校验:检查载入Class文件数据的正确性 b)准备:给类的静态变量分配存储空间 c)解析:将符号引用转成直接引用初始化
:对类的静态变量,静态方法和静态代码块执行初始化工作。
双亲委派模型
:类加载器收到类加载请求,首先将请求委派给父类加载器完成 用户自定义加载器->应用程序加载器->扩展类加载器->启动类加载器。
6、JDK和CGLIB生成动态代理类的区别:
JDK动态代理
只能针对实现了接口的类生成代理(实例化一个类)。此时代理对象和目标对象实现了相同的接口,目标>对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
CGLIB
是针对类实现代理,主要是对指定的类生成一个子类(没有实例化一个类),覆盖其中的方法 。Spring AOP应用场景
性能检测,访问控制,日志管理,事务
等。
默认的策略是如果目标类实现接口,则使用JDK动态代理技术
,如果目标对象没有实现接口,则默认会采用CGLIB代理。
7、强引用,软引用和弱引用的区别
引用 | 区别 |
---|---|
强引用 | 用的最广。我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 |
软引用 | 如果一个对象只具有软引用,则内存空间足够时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。(备注:如果内存不足,随时有可能被回收。) 只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 |
弱引用 | 弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。每次执行GC的时候,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。 |
虚引用 | 虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 |
参考:https://blog.csdn.net/lovesomnus/article/details/47103035
8、Java对象在内存中的状态
状态 | 描述 |
---|---|
可达的/可触及的: | ava对象被创建后,如果被一个或多个变量引用,那就是可达的。即从根节点可以触及到这个对象。其实就是从根节点扫描,只要这个对象在引用链中,那就是可触及的。 |
可恢复的: | Java对象不再被任何变量引用就进入了可恢复状态。在回收该对象之前,该对象的finalize() 方法进行资源清理。如果在finalize() 方法中重新让变量引用该对象,则该对象再次变为可达状态,否则该对象进入不可达状态 |
不可达的: | Java对象不被任何变量引用,且系统在调用对象的finalize() 方法后依然没有使该对象变成可达状态(该对象依然没有被变量引用),那么该对象将变成不可达状态。当Java对象处于不可达状态时,系统才会真正回收该对象所占有的资源。 |
9、判断对象死亡的两种常用算法
算法 | 描述 |
---|---|
引用计数算法 | 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。但是,主流的java虚拟机并没有选用引用计数算法来管理内存,其中最主要的原因是:它很难解决对象之间相互循环引用的问题。 |
根搜索算法:(jvm采用的算法) | 设立若干种根对象,当任何一个根对象(GC Root)到某一个对象均不可达时,则认为这个对象是可以被回收的。 |
10、垃圾回收算法
算法 | 描述 |
---|---|
标记-清除算法 | 标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;清除阶段:清除所有未被标记的对象。 |
复制算法:(新生代的GC) | 将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象。 |
标记-整理算法:(老年代的GC) | 标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。整理阶段:将将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间 |
分代收集算法: | 存活率低:少量对象存活,适合复制算法:在新生代中,每次GC时都发现有大批对象死去,只有少量存活(新生代中98%的对象都是“朝生夕死”),那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成GC。存活率高:大量对象存活,适合用标记-清理/标记-整理:在老年代中,因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”/“标记-整理”算法进行GC。 |
1、详细jvm内存模型
2、讲讲什么情况下回出现内存溢出,内存泄漏?
3、说说Java线程栈
4、JVM 年轻代到年老代的晋升过程的判断条件是什么呢?
5、JVM 出现 fullGC 很频繁,怎么去线上排查问题?
6、类加载为什么要使用双亲委派模式,有没有什么场景是打破了这个模式?
7、类的实例化顺序
8、JVM垃圾回收机制,何时触发MinorGC等操作
9、JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的
10、各种回收器,各自优缺点,重点CMS、G1
11、各种回收算法
12、OOM错误,stackoverflow错误,permgen space错误