1、简要介绍一下JVM虚拟机?
通过软件模拟出来的具有完整的硬件系统功能、运行在完全隔离的环境中的完整的计算机系统。虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
详解直达链接:https://blog.csdn.net/qq_41701956/article/details/81664921
2、简述一次GC的过程?
JVM GC只回收堆区和方法区,堆有存放对象的两大区域,新生代和老年代,新生代又分成三个区域,一个Eden和两个Survivor区,默认空间比例是8:1:1,新实例的对象首先存放在Eden区,当Eden空间不足,则进行一个minorGC,将存活的对象放在其中一个Survivor中,并且每次minorGC对象年龄加一,当这个Survivor中空间不足,将进行minorGC时把存活的对象复制到另一个Survivor,清空此Survivor,当对象的年龄达到阈值时,将把对象放进老年代,当老年代的空间不足时则进行一个FullGC,或当一个大对象(需要大量连续存储空间的对象)进行一次minorGC存活下来,可直接进入老年代,避免Eden与Survivor之间的大量的复制。
持久代,也称之为方法区,用于保存类常量以及字符串常量。注意,这个区域不是用于存储那些从老年代存活下来的对象,这个区域也可能发生GC。发生在这个区域的GC事件也被算为 Major GC 。只不过在这个区域发生GC的条件非常严苛,必须符合以下三种条件才会被回收:
- 1、所有实例被回收;
- 2、加载该类的Class Loader 被回收;
- 3、Class 对象无法通过任何途径访问(包括反射);
3、JMM 内存模型?
JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。
在cpu计算能力飞速发展的情况下,cpu从内存中读取数据已经满足不了cpu的运算速度了,所以需要再引入一层高速缓存来作为内存和处理器之间的缓冲,在Java中,多线程的场景下,系统会为每个线程开辟一个单独的本地内存,同时存在一个存储共享变量的主内存,线程运行时,会将主内存中的数据拷贝到各自的工作内存中,每个工作内存之间对数据的修改是不可见的。使用volatile关键字可是使线程之间的数据修改可见。
4、JVM共享内存都有什么,什么是堆外内存?
方法区和堆是所有线程共享的数据区;
Java有堆内存即Hotspot,堆内存是java语言别与其他语言的优势之一,堆内存完全由JVM负责分配和释放。如果程序没有缺陷代码导致内存泄露,程序员不需要像写C 那样考虑什么时候该释放内存。java中,你只管创建对象,回收内存的事情交给GC。如果有些特殊需求需要自己控制内存分配,还有一个不常用的堆外内存,使用堆外内存,是为了能直接分配和释放内存,提高效率。JDK5.0之后,代码中能直接操作本地内存的方式有2种:使用未公开的Unsafe和NIO包下ByteBuffer。但是,堆外内存几乎是不受GC的管理的,也就是说,需要用户自己调用System.gc()进行内存的释放;
代码语言:javascript复制堆外内存的好处是:
(1)可以扩展至更大的内存空间。比如超过1TB甚至比主存还大的空间;
(2)理论上能减少GC暂停时间;
(3)可以在进程间共享,减少JVM间的对象复制,使得JVM的分割部署更容易实现;
(4)它的持久化存储可以支持快速重启,同时还能够在测试环境中重现生产数据;
5、GC区域?垃圾回收算法?垃圾回收器?G1、CMS、ParNew等垃圾回收器的简介和之间的区别?
详解直达链接:https://www.cnblogs.com/wrencai/p/5668621.html
GC区域:
代码语言:javascript复制 Java堆、方法区;
垃圾收集算法:
代码语言:javascript复制 引用计数算法、可达性算法;
垃圾回收算法:
代码语言:javascript复制 复制算法、标记-清除算法、标记-压缩算法、分代收集算法、分区收集算法;
垃圾回收器:
代码语言:javascript复制新生代GC器:
1)Serial垃圾收集器(单线程串行垃圾收集器,使用复制算法进行垃圾回收,GC时需要暂停所有用户线程,直到GC完成。)
2)ParNew垃圾收集器(多线程串行垃圾收集器,Serial的多线程版本,其它特性同Serial。)
3)Parallel Scavenge垃圾收集器(类似于ParNew,但是该收集器关注的是cpu的吞吐量,通过参数来控制吞吐量,是吞吐量优
先的收集器,同样使用复制算法进行垃圾回收,GC过程需要暂停所有用户线程。)
老年代GC器:
1)Serial和Parallel Scavenge都有对应的老年代版本(Serial Old和Parallel Old垃圾收集器,它们与新生代的区别在于老年代
区域的版本,采用标记-整理算法进行垃圾回收。)
2)CMS垃圾收集器(Concurrent Mark Sweep收集器,是真正意义上的多线程并行垃圾收集器,CMS在GC过程中的某些阶
段用户线程是可以运行的,因此说它是真正意义上的并行垃圾收集器,前面介绍的垃圾收集器在GC过程中都要暂停用户线程,
因此,视它们为与用户线程串行执行的垃圾收集器。)
G1回收器
6、类加载过程?
详解直达链接:https://www.nowcoder.com/questionTerminal/3c9591e7f61343faab53f9288ff6a47f
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking)。
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)。
7、GC、死锁怎么查问题?
详解直达链接:https://blog.csdn.net/weixin_28760063/article/details/81266578
Jconsole: Jconsole是JDK自带的图形化界面工具;
Jstack: Jstack是JDK自带的命令行工具,主要用于线程Dump分析。
8、Synchronized在JDK6做了哪些优化?Synchronized和Lock的区别?
详解直达链接:Synchronized在JDK6做了哪些优化?
Synchronized和Lock的区别?
JDK1.6 对锁的实现引入了大量的优化,如偏向锁(偏向于第一个获得它的线程)、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。
类别 | synchronized | Lock |
---|---|---|
存在层次 | Java的关键字,在jvm层面上 | 是一个类 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 在finally中必须释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 | 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可中断 可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
9、Volatile有什么用?什么是CAS?
详解直达链接:https://blog.csdn.net/happy_bigqiang/article/details/90346029
Volatile作用:
代码语言:javascript复制 轻量级的同步机制,乞丐版的synchronizid
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)
volatile 只能保证对单次读/写的原子性。i 这种操作不能保证原子性。
禁止指令重排,保证特定操作的执行顺序。
CAS 就是比较并交换 – compareAndSwap;
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
CAS的缺点:
代码语言:javascript复制 1、 循环时间长开销大:如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。不断自旋
2、 只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们只能使用循环CAS的方式来保证原子操作,但是,对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。
3、 可能会引发ABA问题:ABA问题其实就是狸猫换太子;产生原因:CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。
10、Java都有哪几种锁?
详解直达链接:https://blog.csdn.net/u010251897/article/details/80840027
自旋锁 ,阻塞锁,可重入锁 ,读写锁 ,互斥锁 ,悲观锁 ,乐观锁 ,公平锁 ,偏向锁, 对象锁,线程锁,锁粗化, 锁消除,轻量级锁,重量级锁,独享锁,共享锁,分段锁。
Synchronized、Lock、ReentrantLock…
11、线程池分几种类型?
详解直达链接:https://www.cnblogs.com/vince66/p/9325638.html
newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor、newScheduleThreadPool、newSingleThreadScheduledExecutor
12、Java乐观锁的实现(CAS 自旋)
13、HashMap是线程安全的么?底层怎么实现的(get,set,resize)?JDK1.8之前和之后做了哪些修改?如果要使得插入kv有序需要使用哪种HashMap?ConcurrentHashMap线程安全是怎么实现的(JDK1.8前后实现不同)?
(1) 不是线程安全的
(2)
(3) hashMap的底层结构在jdk1.7中由数组 链表实现,在jdk1.8中由数组 链表 红黑树实现
JDK1.8之前Put方法:
JDK1.8之后Put方法:
(4) LinkedHashMap,TreeMap
(5)ConcurrentHashMap JDK_1.7使用的就是锁分段技术,ConcurrentHashMap由多个Segment组成(Segment下包含很多Node,也就是我们的键值对了),Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
ConcurrentHashMap实现 1.8 与 JDK 1.7的区别:
代码语言:javascript复制 数据结构:取消了Segment分段锁的数据结构,取而代之的是数组 链表 红黑树的结构。
保证线程安全机制:JDK1.7采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLock。JDK1.8采用CAS Synchronized保证线程安全。
锁的粒度:原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。
定位结点的hash算法简化,会带来弊端:Hash冲突加剧。因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。
查询时间复杂度:从原来的遍历链表O(n),变成遍历红黑树O(logN)。
详解直达链接:https://blog.csdn.net/qq_27139155/article/details/80513419
14、ArrayList和LinkedList的区别?栈和队列的区别?
Arraylist:底层是基于动态数组,根据下表随机访问数组元素的效率高,向数组尾部添加元素的效率高,对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。
Linkedlist基于链表的动态数组,数据添加删除效率高,只需要改变指针指向即可,但是访问数据的平均效率低,需要对链表进行遍历。对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
队列(Queue):先进先出 ;是限定只能在表的一端进行插入和另一端删除操作的线性表 ;基于地址指针进行遍历,而且可以从头部或者尾部进行遍历,但不能同时遍历,无需开辟空间,因为在遍历的过程中不影响数据结构,所以遍历速度要快 ;
栈(Stack):先进后出 ;是限定之能在表的一端进行插入和删除操作的线性表;只能从顶部取数据,也就是说最先进入栈底的,需要遍历整个栈才能取出来,遍历数据时需要微数据开辟临时空间,保持数据在遍历前的一致性;
15、Netty和Jetty区别?
Jetty是Web容器,类似Tomct之类的;
Netty是网络通信的框架,Netty可以支持Http协议,Tcp,Ucp,Netty是一个NIO框架,这个框架的主要目的是为了编写服务器端代码的。
NETTY比JETTY,前者是TCP和UDP这一类SOCKET框架,后者是HTTP框架。
16、Java 静态代理、动态代理?
详解直达链接:http://www.imooc.com/article/257015