Java基础知识大纲

1、HashMap的put方法处理逻辑以及线程不安全体现的场景,基于HashMap实现线程安全该怎么改代码,hashMap在jdk7和jdk8的扩容过程,ConcurrentHashMap的jdk7和jdk8的实现原理

HashMap的底层数据结构、hash冲突如何处理是基础的一定要了解

#1.在jdk1.7中,在多线程环境下,扩容时因为采用头插法,会造成环形链或数据丢失。

#2.在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。

HashMap resize时JAVA8做了优化, 通过(e.hash & oldCap) == 0来判断是否需要移位

ConcurrentHashMap 和JDK6不同,JDK7中除了第一个Segment之外,剩余的Segments采用的是延迟初始化的机制:每次put之前都需要检查key对应的Segment是否为null,如果是则调用ensureSegment()以确保对应的Segment被创建

ConcurrentHashMap在JDK7和JDK8中的不同实现原理


2、ReentrantLock

ReentrantLock  源码分析 :http://www.blogjava.net/zhanglongsr/articles/356782.html

1)等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。

2)公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。

总结

ReentrantLock在采用非公平锁构造时,首先检查锁状态,如果锁可用,直接通过CAS设置成持有状态,且把当前线程设置为锁的拥有者。

如果当前锁已经被持有,那么接下来进行可重入检查,如果可重入,需要为锁状态加上请求数。如果不属于上面两种情况,那么说明锁是被其他线程持有,当前线程应该放入等待队列。 在放入等待队列的过程中,首先要检查队列是否为空队列,如果为空队列,需要创建虚拟的头节点,然后把对当前线程封装的节点加入到队列尾部。由于设置尾部节点采用了CAS,为了保证尾节点能够设置成功,这里采用了无限循环的方式,直到设置成功为止(?这里是不是由于有可能有其他节点同时在尝试加入队列,所以称之为非公平,这里没有保证入队的先后顺序?)。

在完成放入等待队列任务后,则需要维护节点的状态,以及及时清除处于Cancel状态的节点,以帮助垃圾收集器及时回收。如果当前节点之前的节点的等待状态小于1,说明当前节点之前的线程处于等待状态(挂起),那么当前节点的线程也应处于等待状态(挂起)。挂起的工作是由LockSupport类支持的,LockSupport通过JNI调用本地操作系统来完成挂起的任务(java中除了废弃的suspend等方法,没有其他的挂起操作)。在当前等待的线程,被唤起后,检查中断状态,如果处于中断状态,那么需要中断当前线程。


3、类加载机制,能否自定义一个java.lang.Thread

类加载机制详解

类的初始化过程:加载 校验 准备 解析 初始化

双亲委派:向上委托,向下查找

为对象分配空间,分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

内存分配并发问题,2种方案

1)CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。

2)TLAB: 为每一个线程预先在Eden区分配一块儿内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中的剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配

JAVA能否自定义一个加载器? https://blog.csdn.net/sufu1065/article/details/106760850


4、写代码实现OOM和StackOverflow

https://blog.csdn.net/universe_ant/article/details/52996398

1)OOM  对象数量过多

2)SOF 递归不退出 死循环


5、JVM的内存结构,GC的种类及使用场景、GCRoots有哪些、minorGC和FullGC

堆:新生代(eden from to 8:1:1) 老年代

程序计数器

堆:存放对象实例,可能OOM,GC区域

栈:存放局部变量,方法出口等,栈帧出入栈,OOM SOF

方法区:常量,已加载的类信息,静态变量,OOM

JDK8移除永久代,新增元空间,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。

原因:

1)字符串存在永久代中,容易出现性能问题和内存溢出。

2)类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

3)永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

4)Oracle 可能会将HotSpot 与 JRockit 合二为一。

GC调优:https://blog.csdn.net/JAVA_I_want/article/details/103071760

GC优化的核心思路在于:尽可能让对象在新生代中分配和回收,尽量避免过多对象进入老年代,导致对老年代频繁进行垃圾回收,同时给系统足够的内存减少新生代垃圾回收次数。

JVM相关命令:jstat -gc <统计间隔时间> <统计次数>

GC算法:分代处理机制、标记-复制(新生代)、标记-整理/压缩(老年代)、引用计数,各自优缺点

GC收集器种类:

1)serial:单线程,效率高,要停工作线程,新生代复制,老年代标记-整理

2)parallel 

3)concurrent(CMS):标记-清除,老年代, 尽可能让GC线程和用户线程并发执行,以达到降低STW时间的目的

4)G1收集器:最新的,标记-整理/复制,没有内存碎片,可预测停顿,支持新生代和老年代空间的垃圾收集,主要针对配备多核处理器及大容量内存的机器,G1最主要的设计目标是: 实现可预期及可配置的STW停顿时间

Java GC 垃圾回收器的分类和优缺点

HotSpot VM GC 的种类

JVM: GC过程总结(minor GC 和 Full GC)

GC种类:

1)minorGC:新生代gc(复制),eden区满,每发生一次对象年龄+1,超过阈值对象被promote到老年代

2)majorGC:老年代GC(标记-清除/标记-整理),system.gc()/老年代空间不足/方法区空间不足/minorGC后进入老年代的平均大小大于老年代的可用内存

full GC为什么慢:老年代的回收采用标记-压缩算法,是空间换时间的做法。full gc在整个heap上(甚至包括永生代,或者说新的metaspace)

关于GC中的STW

这几个都是 GCRoots:

1)两个栈: Java栈 和 Native 栈中所有引用的对象;

2)两个方法区:方法区中的常量和静态变量;

3)所有线程对象;

4)所有跨代引用对象;

5)和已知 GCRoots 对象同属一个CardTable 的其他对象。


6、反射的原理、应用场景

https://zhuanlan.zhihu.com/p/162971344

调用反射的总体流程如下:

1)准备阶段:编译期装载所有的类,将每个类的元信息保存至Class类对象中,每一个类对应一个Class对象

2)获取Class对象:调用x.class/x.getClass()/Class.forName() 获取x的Class对象clz(这些方法的底层都是native方法,是在JVM底层编写好的,涉及到了JVM底层,就先不进行探究了)

3)进行实际反射操作:通过clz对象获取Field/Method/Constructor对象进行进一步操作

4)Class类中包含的ReflectionData,用于保存进行反射操作的基础信息

应用场景:

Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。

这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射。

另外,像 Java 中的一大利器 注解 的实现也用到了反射。

反射会有性能问题,最终调用的都是native方法,解决方案:

1)对通过反射调用获得的Class、Method、Field实例进行缓存,避免多次Dynamic Resolve。 

2)Java 7开始提供了java.lang.invoke.MethodHandle类,MethodHandle类的安全性验证在获取实例时进行而不是每次调用时都要进行验证,减小开销。 

3)使用Runtime创建的类


7、Lsit、Set、Map数据结构

https://blog.csdn.net/qq_28033239/article/details/98204664

1)List

Arraylist: Object数组(动态数组)

Vector: Object数组

LinkedList: 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环)

2)Set

HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素,查找O(1)

LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。

TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树,查找O(logn))

3)Map

TreeMap的红黑树详解

红黑树特点:根节点黑色、红色节点的子节点为黑色、任意节点到叶子节点经过的黑色节点相同、叶子节点为黑色


8、String、StringBuilder、StringBuffer区别

1)String实现了equals()方法和hashCode()方法,new String("java").equals(new String("java"))的结果为true;而StringBuffer没有实现equals()方法和hashCode()方法,因此,new StringBuffer("java").equals(new StringBuffer("java"))的结果为false,将StringBuffer对象存储进Java集合类中会出现问题。

2)StringBuilder是线程不安全的,StringBuffer是线程安全的


9、Java IO/NIO/AIO

https://www.cnblogs.com/sxkgeek/p/9488703.html

区别:

1)标准IO

基于阻塞的 I/O 操作:在等待数据写入或写入都会引起阻塞,即当线程调用 write(), read() 时,线程会被阻塞,直到数据被完全读取或写入;

面向流:使用流来传输数据,这种方式的 I/O 操作速度比较慢;

2)NIO

NIO详解:https://www.cnblogs.com/pony1223/p/8138233.html

基于非阻塞的 I/O 操作(同步非阻塞):不等待数据的读取或写入,允许调用线程请求向通道写入数据,而不等待数据被完全写入;NIO之所以是同步,是因为它的accept/read/write方法的内核I/O操作都会阻塞当前线程

面向缓存:使用通道(channel)和缓冲区(buffer)来传输数据,数据被读取入缓冲区,使用通道进一步进行处理;

3)AIO

基于回调和事件机制,在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种IO模型。

Java NIO 系统的核心组件

通道和缓冲区(Channel and Buffer):NIO 使用通道和缓冲区传输数据,数据总是从缓冲区写入通道,并从通道读取到缓冲区;

选择器(Selectors):NIO 的选择器用于监视多个通道的状态(如数据到达,连接打开等),通过选择器,单线程可以监视多个通道中的数据;


10、工厂+策略模式

工厂+策略:https://blog.csdn.net/weixin_36167744/article/details/110522413

各种设计模式:https://www.runoob.com/design-pattern/abstract-factory-pattern.html


11、序列化与反序列化

https://blog.csdn.net/Cavewang/article/details/119257739

writeObject和readObject——重写这2个方法来自定义序列化和反序列化

考察一个序列化框架一般会关注以下几点:

1)Encoding format。是 human readable 还是 binary。

2)Schema declaration。也叫作契约声明,基于 IDL,比如 Protocol Buffers/Thrift,还是自描述的,比如 JSON、XML。另外还需要看是否是强类型的。

3)语言平台的中立性。比如 Java 的 Native Serialization 就只能自己玩,而 Protocol Buffers 可以跨各种语言和平台。

4)新老契约的兼容性。比如 IDL 加了一个字段,老数据是否还可以反序列化成功。

5)和压缩算法的契合度。跑 benchmark 和实际应用都会结合各种压缩算法,例如 gzip、snappy。

6)性能。这是最重要的,序列化、反序列化的时间,序列化后数据的字节大小是考察重点。


12、JVM内存调优常用命令

https://blog.csdn.net/lin_litao/article/details/80788919

1)jps :JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程 (用这个查进程IP)

2)jstat(JVM statistics Monitoring):是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。(-gc  -gcutil  -gccause)

3)jmap(JVM Memory Map):用于生成heap dump文件,如果不使用这个命令,还阔以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候·自动生成dump文件。 jmap不仅能生成dump文件,还阔以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。

4)jhat(JVM Heap Analysis Tool):是与jmap搭配使用,用来分析jmap生成的dump

5)jstack:用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

6)jinfo(JVM Configuration info):这个命令作用是实时查看和调整虚拟机运行参数。 之前的jps -v口令只能查看到显示指定的参数,如果想要查看未被显示指定的参数的值就要使用jinfo口令


13、OOM了怎么排查

https://mp.weixin.qq.com/s/bClGUG32QQmdgtnpyo431Q

可能原因:

(1)内存确实分配过小,内存确实不够用;(2)某一个对象被频繁申请,却没有释放,内存不断泄漏,导致内存耗尽;(3)某一个资源被频繁申请,系统资源耗尽,例如:不断创建线程,不断发起网络连接;

检查方法:

1)确认是不是内存本身就分配过小方法:jmap -heap + PID

2)找到最耗内存的对象方法:jmap -histo:live PID | more

会打印(1)实例数;(2)所占内存大小;(3)类名;

3)确认是否是资源耗尽工具:(1)pstree(2)netstat

https://blog.csdn.net/ywlmsm1224811/article/details/91866707

OOM一般有以下两种情况:

1、年老代堆空间被占满

异常:java.lang.OutOfMemoryError:java  heap space

说明:这是最典型的内存泄漏方式,简单说就是所有堆空间都被无法回收的垃圾对象占满,虚拟机再也无法分配新空间

解决方案:这种方式解决起来比较简单,一般就是根据垃圾回收前后的情况对比,同时根据对象引用情况(常见的集合对象引用)分析,基本都可以找到泄漏点。

2、持久代被占满

异常:java.lang.OutOfMemoryError:PermGen space

说明:Perm 空间被占满,无法为新的 class 分配存储空间而引发的异常。这个异常以前是没有的,但是在 java 大量使用反射的今天这个异常就比较常见了。主要原因是大量动态反射生成的类不断被加载,最终导致 Perm 区被占满。更可怕的是,不同的 classLoader 即便使用相同的类,但是都会对其进行加载,相当于同一个东西,如果有 N 个classLoader 那么它将会被加载 N 次。因此,在某些情况下,这个问题基本视为无解,当然,存在大量 classLoader 和大量反射类的情况并不多

解决方案:增加持久代内存 ,例如:-XX:MaxPermSize=16M


14、设计模式六(七?)大原则

面向对象的设计模式有七大基本原则:(SOLID)

开闭原则(首要原则):要对扩展开放,对修改关闭

单一职责原则:实现类要职责单一

里氏代换原则:不要破坏继承体系

依赖倒转原则:面向接口编程

接口隔离原则:设计接口要精简单一

合成/聚合复用原则:尽量使用聚合,组合,而不是继承

最少知识原则或者迪米特法则:降低耦合


15、Java中的引用类型

https://www.jianshu.com/p/fcc09b2eb006

Java中存在四种引用,它们由强到弱依次是:强引用、软引用、弱引用、虚引用。


16、JDK动态代理和CGLib的区别

www.cnblogs.com/sandaman2019/p/12636727.html

1)JDK动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

2) Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理

Cglib比JDK快?

1)cglib底层是ASM字节码生成框架,但是字节码技术生成代理类,在JDL1.6之前比使用java反射的效率要高

2)在jdk6之后逐步对JDK动态代理进行了优化,在调用次数比较少时效率高于cglib代理效率

3)只有在大量调用的时候cglib的效率高,但是在1.8的时候JDK的效率已高于cglib

4)Cglib不能对声明final的方法进行代理,因为cglib是动态生成代理对象,final关键字修饰的类不可变只能被引用不能被修改

Spring如何选择是用JDK还是cglib?

1)当bean实现接口时,会用JDK代理模式

2)当bean没有实现接口,用cglib实现

3)可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)


17、深拷贝、浅拷贝

www.cnblogs.com/xinruyi/p/11537963.html

浅拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。

深拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是值类型的还是引用类型,都复制独立的一份。当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。

Object 类提供的 clone 是只能实现 浅拷贝的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容