不是吧阿sir,阿里面试官必问

​ final修饰类:

​ final 修饰类时,表明这个类不能被继承。final 类中的所有成员⽅法都会被隐式地指定为 final ⽅法。

10、序列化和反序列化。反序列化失败的场景。

​ 序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值再生成相同状态的对象。对象序列化是对象持久化的一种实现方法,它是将对象的属性和方法转化为一种序列化的形式用于存储和传输。反序列化就是根据这些保存的信息重建对象的过程。

序列化: 将java对象转化为字节序列的过程。

反序列化: 将字节序列转化为java对象的过程。

优点:

​ a、实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里)

​ b、利用序列化实现远程通信,即在网络上传送对象的字节序列。

反序列化失败的场景:

​ 序列化ID:serialVersionUID不一致的时候,导致反序列化失败

11、ArrayList和LinkedList的区别和底层实现?如何实现线程安全?

白嫖资料

ArrayList:

​ 底层基于数组实现,支持对元素进行快速随机访问,支持元素重复;默认初始大小为10,当数组容量不够时,会触发扩容机制(扩大到当前的1.5倍),需要将原来数组的数据复制到新的数组中;当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。

LinkedList:

​ 底层基于双向链表实现,适合数据的动态插入和删除;内部提供了 List 接口中没有定义的方法,用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

ArrayList与LinkedList区别:

​ 都是线程不安全的,ArrayList 适用于查找的场景,LinkedList 适用于 增加、删除多的场景

实现线程安全:

​ 可以使用原生的Vector,或者是Collections.synchronizedList(List list)函数返回一个线程安全的ArrayList集合,或者使用concurrent并发包下的CopyOnWriteArrayList的。

​ ①、Vector: 底层通过synchronize修饰保证线程安全,效率较差

​ ② 、Collections.synchronizedList(List list):


//使用Collections.synchronizedList(List list)方法实现线程安全

List<?> list=Collections.synchronizedList(new ArrayList<>()); 

​ ③、CopyOnWriteArrayList:写时加锁,使用了一种叫写时复制的方法;读操作是可以不用加锁的

12、List遍历时如何删除元素?fail—fast是什么?fail—safe是什么?

①、普通for循环遍历List删除指定元素


for(int i=0; i < list.size(); i++){

   if(list.get(i) == 5) 

       list.remove(i);

} 

② 、迭代遍历,用list.remove(i)方法删除元素


Iterator<Integer> it = list.iterator();

while(it.hasNext()){

    Integer value = it.next();

    if(value == 5){

        list.remove(value);

    }

} 

③、foreach遍历List删除元素


for(Integer i:list){

    if(i==3) list.remove(i);

} 

fail—fast: 快速失败

​ 当异常产生时,直接抛出异常,程序终止;

​ fail-fast只要是体现在当我们在遍历集合元素的时候,经常会使用迭代器,但在迭代器遍历元素的过程中,如果集合的结构被改变的话,就会抛出异常ConcurrentModificationException,防止继续遍历。这就是所谓的快速失败机制。这里要注意的这里说的结构被改变,是例如插入和删除这种操作,只是改变集合里的值的话并不会抛出异常。

fail—safe: 安全失败

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException。

缺点:基于拷贝内容的优点是避免了ConcurrentModificationException,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

13、详细介绍HashMap。

角度:数据结构+扩容情况+put查找的详细过程+哈希函数+容量为什么始终都是2^N,JDK1.7与1.8的区别。

数据结构:

​ HashMap在底层数据结构上采用了数组+链表+红黑树,通过散列映射来存储键值对数据

扩容情况:

​ 默认的负载因子是0.75,表示的是,如果数组中已经存储的元素个数大于数组长度的75%,将会引发扩容操作。

​ 【1】创建一个长度为原来数组长度两倍的新数组。

​ 【2】重新对原数组中的Entry对象进行哈希运算,以确定他们各自在新数组中的新位置。

put操作步骤:

​ 1、判断数组是否为空,为空进行初始化;

​ 2、不为空,则计算 key 的 hash 值,通过(n - 1) & hash计算应当存放在数组中的下标 index;

​ 3、查看 table[index] 是否存在数据,没有数据就构造一个Node节点存放在 table[index] 中;

​ 4、存在数据,说明发生了hash冲突(存在二个节点key的hash值一样), 继续判断key是否相等,相等,用新的value替换原数据;

​ 5、若不相等,判断当前节点类型是不是树型节点,如果是树型节点,创造树型节点插入红黑树中;

​ 6、若不是红黑树,创建普通Node加入链表中;判断链表长度是否大于 8,大于则将链表转换为红黑树;

​ 7、插入完成之后判断当前节点数是否大于阈值,若大于,则扩容为原数组的二倍

哈希函数:

白嫖资料

​ hash函数是先拿到 key 的hashcode,是一个32位的值,然后让hashcode的高16位和低16位进行异或操作。该函数也称为扰动函数,做到尽可能降低hash碰撞。

容量为什么始终都是2^N:

​ 为了能让 HashMap 存取⾼效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上⾯也讲到了过了,Hash 值的范围值-2147483648到2147483647,前后加起来⼤概40亿的映射空间,只要哈希函数映射得⽐较均匀松散,⼀般应⽤是很难出现碰撞的。但问题是⼀个40亿⻓度的数组,内存是放不下的。所以这个散列值是不能直接拿来⽤的。⽤之前还要先做对数组的⻓度取模运算,得到的余数才能⽤来要存放的位置也就是对应的数组下标。这个数组下标的计算⽅法是“ (n - 1) & hash ”。(n代表数组⻓度)。这也就解释了 HashMap 的⻓度为什么是2的幂次⽅。

JDK1.7与1.8的区别:

JDK1.7 HashMap:

​ 底层是 数组和链表 结合在⼀起使⽤也就是 链表散列。HashMap 通过 key 的hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这⾥的 n 指的是数组的⻓度),如果当前位置存在元素的话,就判断该元素与要存⼊的元素的 hash值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。

DK1.8 HashMap:

​ HashMap在底层数据结构上采用了数组+链表+红黑树,通过散列映射来存储键值对数据;当链表⻓度⼤于阈值(默认为 8),数组的⻓度大于 64时,将链表转化为红⿊树,以减少搜索时间

14、HashMap如何实现线程安全?ConcurrentHashMap的底层实现?JDK1.7与JDK1.8的区别

白嫖资料

​ 可以通过ConcurrentHashMap 和 Hashtable来实现线程安全;Hashtable 是原始API类,通过synchronize同步修饰,效率低下;ConcurrentHashMap 通过分段锁实现,效率较比Hashtable要好;

ConcurrentHashMap的底层实现:

​ JDK1.7的 ConcurrentHashMap 底层采⽤ 分段的数组+链表 实现;采用 分段锁(Sagment) 对整个桶数组进⾏了分割分段(Segment),每⼀把锁只锁容器其中⼀部分数据,多线程访问容器⾥不同数据段的数据,就不会存在锁竞争,提⾼并发访问率。

在这里插入图片描述

​ JDK1.8的 ConcurrentHashMap 采⽤的数据结构跟HashMap1.8的结构⼀样,数组+链表/红⿊⼆叉树;摒弃了Segment的概念,⽽是直接⽤ Node 数组+链表+红⿊树的数据结构来实现,通过并发控制 synchronized 和CAS来操作保证线程的安全。

15、正则表达式会写吗?

​ 正则通过一些特定的符号与数字来表示一串字符,其中有:元字符、重复限定符、分组、转义、条件或、区间;

16、设计模式了解吗?

单例模式、工厂模式、代理模式

17、linux指令知道哪些?

文件管理:ls、cd、touch创建普通文件、rm删除、mkdir新建目录、mv移动、cp拷贝、chmod修改权限

进程管理:ps显示进程信息、kill杀死进程

系统管理:top、free显示系统运行信息、vmstat输出各资源使用情况

网络通讯:ping测试网络连通性、netstat显示网络相关信息

18、JVM相关

1、JVM运行时内存划分?

JVM运行时数据区域: 堆、方法区(元空间)、虚拟机栈、本地方法栈、程序计数器

在这里插入图片描述

白嫖资料

Heap(堆):

​ 对象的实例以及数组的内存都是要在堆上进行分配的,堆是线程共享的一块区域,用来存放对象实例,也是垃圾回收(GC)的主要区域;

​ 堆细分:新生代、老年代,对于新生代又分为:Eden区和Surviver1和Surviver2区;

方法区:

​ 对于JVM的方法区也可以称之为永久区,它储存的是已经被java虚拟机加载的类信息、常量、静态变量;Jdk1.8以后取消了方法区这个概念,称之为元空间(MetaSpace);

虚拟机栈:

​ 虚拟机栈是线程私有的,他的生命周期和线程的生命周期是一致的。里面装的是一个一个的栈帧,每一个方法在执行的时候都会创建一个栈帧,栈帧中用来存放(局部变量表、操作数栈 、动态链接 、返回地址);在Java虚拟机规范中,对此区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将会抛出StackOverflowError异常;如果虚拟机栈动态扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

  • 局部变量表:局部变量表是一组变量值存储空间,用来存放方法参数、方法内部定义的局部变量。局部变量表的容量是以变量槽(variable

    slot)为最小的单位。Java虚拟机没有明确规定一个slot所占的空间大小。只是导向性的说了每一个slot能存放8种基本数据类型中的一种(long

    和double这种64位的需要两个slot);

  • 操作数栈:是用来记录一个方法在执行的过程中,字节码指令向操作数栈中进行入栈和出栈的过程。大小在编译的时候已经确定了,当一个方法刚开始执行的时候,操作数栈中是空发的,在方法执行的过程中会有各种字节码指令往操作数栈中入栈和出栈。

  • 动态链接:因为字节码文件中有很多符号的引用,这些符号引用一部分会在类加载的解析阶段或第一次使用的时候转化成直接引用,这种称为静态解析;另一部分会在运行期间转化为直接引用,称为动态链接。

返回地址(returnAddress): 类型(指向了一条字节码指令的地址)

本地方法栈:

​ 本地方法栈和虚拟机栈类似,不同的是虚拟机栈服务的是Java方法,而本地方法栈服务的是Native方法。在HotSpot虚拟机实现中是把本地方法栈和虚拟机栈合二为一的,同理它也会抛出StackOverflowError和OOM异常。

PC程序计数器:

​ PC,指的是存放下一条指令的位置的这么一个区域。它是一块较小的内存空间,且是线程私有的。由于线程的切换,CPU在执行的过程中,一个线程执行完了,接下来CPU切换到另一个线程去执行,另外一个线程执行完再切回到之前的线程,这时需要记住原线程的下一条指令的位置,所以每一个线程都需要有自己的PC。

2、堆内存分配策略
  • 对象优先分配在Eden区,如果Eden区没有足够的空间进行分配时,虚拟机执行一次MinorGC。而那些无需回收的存活对象,将会进到

    Survivor 的 From 区(From 区内存不足时,直接进入 Old 区)。

  • 大对象直接进入老年代(需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。

  • 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄(Age Count)计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,直到达到阀值(默认15次),对象进入老年区。

  • 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。

3、Full GC触发条件

​ 每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小,则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC

4、如何判断对象是否存活?回收对象的两次标记过程。

引用计数法:

​ 给对象添加一个引用计数器,每当由一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。

​ 优点:实现简单,判定效率也很高

​ 缺点:他很难解决对象之间相互循环引用的问题。

白嫖资料

对象可达性:

​ 通过一系列的成为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC ROOTS没有任何引用链相连时,则证明此对象时不可用的;

两次标记过程:

​ 对象被回收之前,该对象的finalize()方法会被调用;两次标记,即第一次标记不在“关系网”中的对象。第二次的话就要先判断该对象有没有实现finalize()方法了,如果没有实现就直接判断该对象可回收;如果实现了就会先放在一个队列中,并由虚拟机建立的一个低优先级的线程去执行它,随后就会进行第二次的小规模标记,在这次被标记的对象就会真正的被回收了。

5、垃圾回收算法以及垃圾回收器介绍,尤其是G1和CMS的优缺点

垃圾回收算法:复制算法、标记清除、标记整理、分代收集

复制算法:

​ 将内存分为⼤⼩相同的两块,每次使⽤其中的⼀块。当这⼀块的内存使⽤完后,就将还存活的对象复制到另⼀块去,然后再把使⽤的空间⼀次清理掉。这样就使每次的内存回收都是对内存区间的⼀半进⾏回收;

​ 优点:实现简单,内存效率高,不易产生碎片

​ 缺点:内存压缩了一半,倘若存活对象多,Copying 算法的效率会大大降低

标记清除:

​ 标记出所有需要回收的对象,在标记完成后统⼀回收所有被标记的对象

​ 缺点:效率低,标记清除后会产⽣⼤量不连续的碎⽚,可能发生大对象不能找到可利用空间的问题。

标记整理:

​ 标记过程仍然与“标记-清除”算法⼀样,再让所有存活的对象向⼀端移动,然后直接清理掉端边界以外的内存;解决了产生大量不连续碎片问题

分代收集:

​ 根据各个年代的特点选择合适的垃圾收集算法。

​ 新生代采用复制算法,新生代每次垃圾回收都要回收大部分对象,存活对象较少,即要复制的操作比较少,一般将新生代划分为一块较大的 Eden 空间和两个较小的 Survivor 空间(From Space, To Space),每次使用Eden 空间和其中的一块 Survivor 空间,当进行回收时,将该两块空间中还存活的对象复制到另一块 Survivor 空间中。

​ 老年代的对象存活⼏率是⽐较⾼的,⽽且没有额外的空间对它进⾏分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进⾏垃圾收集。

**垃圾收集器:**Serial、Parnew、parallel Scavenge、Serialold 、Parnewold、CMS、G1

Serial:

​ Serial 是一个单线程的收集器,它不但只会使用一个 CPU 或一条线程去完成垃圾收集工作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束。

Parnew:

​ ParNew 垃圾收集器其实是 Serial 收集器的多线程版本,也使用复制算法,除了使用多线程进行垃圾收集之外,其余的行为和 Serial 收集器完全一样,ParNew 垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程。

parallel Scavenge:

白嫖资料

​ Parallel Scavenge收集器关注点是吞吐量(⾼效率的利⽤CPU)。CMS等垃圾收集器的关注点更多的是⽤户线程的停顿时间(提⾼⽤户体验);高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。

Serial old:

Serial收集器的⽼年代版本,它同样是⼀个单线程收集器,使用标记-整理算法。主要有两个用途:

在 JDK1.5 之前版本中与新生代的 Parallel Scavenge 收集器搭配使用。

作为年老代中使用 CMS 收集器的后备垃圾收集方案。

parallel old:

​ Parallel Scavenge收集器的⽼年代版本。使⽤多线程和“标记-整理”算法。

CMS: 重要

​ CMS收集器是一种年老代垃圾收集器,其最主要目标是获取最短垃圾回收停顿时间,和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法。最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。CMS 工作机制相比其他的垃圾收集器来说更复杂,整个过程分为以下 4 个阶段:

初始标记: 只是标记一下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。

并发标记: 进 行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。

重新标记: 为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。

并发清除: 清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看CMS 收集器的内存回收和用户线程是一起并发地执行。

优点: 并发收集、低停顿

缺点: 对CPU资源敏感;⽆法处理浮动垃圾;使⽤“标记清除”算法,会导致⼤量空间碎⽚产⽣。

G1: 重要

​ 是⼀款⾯向服务器的垃圾收集器,主要针对配备多颗处理器及⼤容量内存的机器.以极⾼概率满⾜GC停顿时间要求的同时,还具备⾼吞吐量性能特征;相比与 CMS 收集器,G1 收集器两个最突出的改进是:

​ 【1】基于标记-整理算法,不产生内存碎片。

​ 【2】可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。

​ G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收集效率。

6、创建一个对象的步骤

步骤:类加载检查、分配内存、初始化零值、设置对象头、执行init方法

①类加载检查:

​ 虚拟机遇到⼀条 new 指令时,⾸先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引⽤,并且检查这个符号引⽤代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执⾏相应的类加载过程。

②分配内存:

​ 在类加载检查通过后,接下来虚拟机将为新⽣对象分配内存。对象所需的内存⼤⼩在类加载完成后便可确定,为对象分配空间的任务等同于把⼀块确定⼤⼩的内存从 Java 堆中划分出来。分配⽅式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配⽅式由 Java 堆是否规整决定,⽽Java堆是否规整⼜由所采⽤的垃圾收集器是否带有压缩整理功能决定。

白嫖资料

③初始化零值:

​ 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值,这⼀步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使⽤,程序能访问到这些字段的数据类型所对应的零值。

④设置对象头:

​ 初始化零值完成之后,虚拟机要对对象进⾏必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运⾏状态的不同,如是否启⽤偏向锁等,对象头会有不同的设置⽅式。

⑤执⾏ init ⽅法:

​ 在上⾯⼯作都完成之后,从虚拟机的视⻆来看,⼀个新的对象已经产⽣了,但从Java 程序的视⻆来看,对象创建才刚开始, ⽅法还没有执⾏,所有的字段都还为零。所以⼀般来说,执⾏ new 指令之后会接着执⾏ ⽅法,把对象按照程序员的意愿进⾏初始化,这样⼀个真正可⽤的对象才算完全产⽣出来。

7、详细介绍类加载过程

过程:加载、验证、准备、解析、初始化

在这里插入图片描述

加载阶段:

​ 1.通过一个类的全限定名来获取定义此类的二进制字节流。

​ 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

​ 3.在Java堆中生成一个代表这个类的java.lang.class对象,作为方法区这些数据的访问入口。

验证阶段:

​ 1.文件格式验证(是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理)

​ 2.元数据验证(对字节码描述的信息进行语意分析,以保证其描述的信息符合Java语言规范要求)

​ 3.字节码验证(保证被校验类的方法在运行时不会做出危害虚拟机安全的行为)

​ 4.符号引用验证(虚拟机将符号引用转化为直接引用时,解析阶段中发生)

准备阶段:

​ 准备阶段是正式为类变量分配内存并设置类变量初始值的阶段。将对象初始化为“零”值

解析阶段:

​ 解析阶段时虚拟机将常量池内的符号引用替换为直接引用的过程。

初始化阶段:

​ 初始化阶段时加载过程的最后一步,而这一阶段也是真正意义上开始执行类中定义的Java程序代码。

8、双亲委派机制,使用这个机制的好处?如何破坏?

​ 每⼀个类都有⼀个对应它的类加载器。系统中的 ClassLoder 在协同⼯作的时候会默认使⽤ 双亲委派模型 。即在类加载的时候,系统会⾸先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,⾸先会把该请求委派该⽗类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当⽗类加载器⽆法处理时,才由⾃⼰来处理。当⽗类加载器为null时,会使⽤启动类加载器 BootstrapClassLoader 作为⽗类加载器。

使用好处:

​ 此机制保证JDK核心类的优先加载;使得Java程序的稳定运⾏,可以避免类的重复加载,也保证了 Java 的核⼼ API 不被篡改。如果不⽤没有使⽤双亲委派模型,⽽是每个类加载器加载⾃⼰的话就会出现⼀些问题,⽐如我们编写⼀个称为 java.lang.Object 类的话,那么程序运⾏的时候,系统就会出现多个不同的Object 类。

白嫖资料

破坏双亲委派机制:

​ 可以⾃⼰定义⼀个类加载器,重写loadClass方法;

9、了解下tomcat的类加载机制

步骤:

  • 先在本地cache查找该类是否已经加载过,看看 Tomcat 有没有加载过这个类。

  • 如果Tomcat 没有加载过这个类,则从系统类加载器的cache中查找是否加载过。

  • 如果没有加载过这个类,尝试用ExtClassLoader类加载器类加载,重点来了,这里并没有首先使用 AppClassLoader 来加载类。这个Tomcat 的 WebAPPClassLoader 违背了双亲委派机制,直接使用了 ExtClassLoader来加载类。这里注意 ExtClassLoader 双亲委派依然有效,ExtClassLoader 就会使用 Bootstrap ClassLoader 来对类进行加载,保证了 Jre 里面的核心类不会被重复加载。 比如在 Web 中加载一个 Object 类。WebAppClassLoader → ExtClassLoader → Bootstrap ClassLoader,这个加载链,就保证了 Object 不会被重复加载。

  • 如果 BoostrapClassLoader,没有加载成功,就会调用自己的 findClass 方法由自己来对类进行加载,findClass 加载类的地址是自己本 web 应用下的 class。

    加载依然失败,才使用 AppClassLoader 继续加载。

    都没有加载成功的话,抛出异常。

  • 总结一下以上步骤,WebAppClassLoader 加载类的时候,故意打破了JVM 双亲委派机制,绕开了 AppClassLoader,直接先使用 ExtClassLoader 来加载类。

10、JVM性能调优,常用命令,以及工具

对应进程的JVM状态以定位问题和解决问题并作出相应的优化

常用命令: jps、jinfo、jstat、jstack、jmap

jps:查看java进程及相关信息


jps -l 输出jar包路径,类全名

jps -m 输出main参数

jps -v 输出JVM参数 

jinfo:查看JVM参数


jinfo 11666

jinfo -flags 11666 

jstat:查看JVM运行时的状态信息,包括内存状态、垃圾回收


命令格式:

jstat [option] LVMID [interval] [count]

其中LVMID是进程id,interval是打印间隔时间(毫秒),count是打印次数(默认一直打印)



option参数解释:

-class class loader的行为统计

-compiler HotSpt JIT编译器行为统计

-gc 垃圾回收堆的行为统计

-gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计

-gcutil 垃圾回收统计概述

-gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因

-gcnew 新生代行为统计

-gcnewcapacity 新生代与其相应的内存空间的统计

-gcold 年老代和永生代行为统计

-gcoldcapacity 年老代行为统计

-gcpermcapacity 永生代行为统计

-printcompilation HotSpot编译方法统计 

jstack:查看JVM线程快照,jstack命令可以定位线程出现长时间卡顿的原因,例如死锁,死循环


命令格式:

jstack [-l] <pid> (连接运行中的进程)

jstack -F [-m] [-l] <pid> (连接挂起的进程)

jstack [-m] [-l] <executable> <core> (连接core文件)

jstack [-m] [-l] [server_id@]<remote server IP or hostname> (连接远程debug服务器) 

option参数解释:


-F 当使用jstack <pid>无响应时,强制输出线程堆栈。

-m 同时输出java和本地堆栈(混合模式)

-l 额外显示锁信息 

jmap:可以用来查看内存信息


命令格式:

jmap [option] <pid> (连接正在执行的进程)

jmap [option] <executable <core> (连接一个core文件)

jmap [option] [server_id@]<remote server IP or hostname> (链接远程服务器)



option参数解释:

<none> to print same info as Solaris pmap

-heap 打印java heap摘要

-histo[:live] 打印堆中的 java对象统计信息

-clstats 打印类加载器统计信息

-finalizerinfo 打印在f-queue中等待执行finalizer方法的对象

-dump:<dump-options> 生成java堆的dump文件


**Java面试核心知识点笔记**

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

![蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6](https://img-blog.csdnimg.cn/img_convert/cca8e7893b62cb259275a0395ee569eb.png)

**Java中高级面试高频考点整理**

![蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6](https://img-blog.csdnimg.cn/img_convert/f66d5e1f5c7d46e5c16860c28c77dc6b.png)

**[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](https://codechina.csdn.net/m0_60958482/java-p7)**

![蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6](https://img-blog.csdnimg.cn/img_convert/503c4c46126c8631938c92a2feb767bc.png)

**最后分享Java进阶学习及面试必备的视频教学**

![蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6](https://img-blog.csdnimg.cn/img_convert/a41cc37f0d1025a196b2d8c92e185c45.png)
e server IP or hostname> (连接远程debug服务器) 

option参数解释:


-F 当使用jstack <pid>无响应时,强制输出线程堆栈。

-m 同时输出java和本地堆栈(混合模式)

-l 额外显示锁信息 

jmap:可以用来查看内存信息


命令格式:

jmap [option] <pid> (连接正在执行的进程)

jmap [option] <executable <core> (连接一个core文件)

jmap [option] [server_id@]<remote server IP or hostname> (链接远程服务器)



option参数解释:

<none> to print same info as Solaris pmap

-heap 打印java heap摘要

-histo[:live] 打印堆中的 java对象统计信息

-clstats 打印类加载器统计信息

-finalizerinfo 打印在f-queue中等待执行finalizer方法的对象

-dump:<dump-options> 生成java堆的dump文件


**Java面试核心知识点笔记**

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

[外链图片转存中...(img-TMkLz5aF-1631092077912)]

**Java中高级面试高频考点整理**

[外链图片转存中...(img-9HsHaQr8-1631092077914)]

**[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](https://codechina.csdn.net/m0_60958482/java-p7)**

[外链图片转存中...(img-M81NrTk6-1631092077915)]

**最后分享Java进阶学习及面试必备的视频教学**

[外链图片转存中...(img-X7zaGg4c-1631092077917)]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值