Java⾯试题及答案整理(2022最新版)持续更新
发现⽹上很多Java⾯试题都没有答案,所以花了很长时间搜集整理出来了这套Java⾯试题⼤全,希望对⼤家有帮助哈~博主已将这些⾯试题整理到⼀个⽹站上,每天更新 Java ⾯试题,⽬前有 1万多道 Java ⾼频⾯试题。
本套Java⾯试题⼤全,汇总了⼤量经典的Java程序员⾯试题以及答案,包含Java语⾔常见⾯试题、Java⼯程师⾼级⾯试题及⼀些⼤⼚Java开发⾯试宝典
这套7000多页的Java⾯试题 PDF ⼤全,希望对⼤家有帮助哈~
CMS已经弃⽤。⽣活美好,时间有限,不建议再深⼊研究了。如果碰到问题,直接祭出回收过程即可。
1、 初始标记
2、 并发标记
3、 并发预清理java常见笔试题
4、 并发可取消的预清理
5、 重新标记
6、 并发清理
由于《深⼊理解java虚拟机》⼀书的流⾏,⾯试时省略3、4步⼀般也是没问题的。
sleep()⽅法(休眠)是线程类(Thread)的静态⽅法,调⽤此⽅法会让当前线程暂停执⾏指定的时间,将执⾏机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会⾃动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object 类的⽅法,调⽤对象的wait()⽅法导致当前线程放弃对象的锁(线程暂停执⾏),进⼊对象的等待池(wait pool),只有调⽤对象的
notify()⽅法(或notifyAll()⽅法)时才能唤醒等待池中的线程进⼊等锁池(lock pool),如果线程重新获得对象的锁就可以进⼊就绪状态。
补充:可能不少⼈对什么是进程,什么是线程还⽐较模糊,对于为什么需要多线程编程也不是特别理解。简单的说:进程是具有⼀定独⽴功能的程序关于某个数据集合上的⼀次运⾏活动,是操作系统进⾏资源分配和调度的⼀个独⽴单位;线程是进程的⼀个实体,是CPU调度和分派的基本单位,是⽐进程更⼩的能独⽴运⾏的基本单位。线程的划分尺度⼩于进程,这使得多线程程序的并发性⾼;进程在
执⾏时通常拥有独⽴的内存单元,⽽线程之间可以共享内存。使⽤多线程的编程通常能够带来更好的性能和⽤户体验,但是多线程的程序对于其他程序是不友好的,因为它可能占⽤了更多的CPU资源。当然,也不是线程越多,程序的性能就越好,因为线程之间的调度和切换也会浪费CPU时间。时下很时髦的 就采⽤了单线程异步I/O的⼯作模式。
必须遵循isapi_redirector.dll的标准指令
配置IIS使⽤“集成windows验证”
确保在服务器.xml中您已经禁⽤了tomcat⾝份验证excel两张表匹配vlookup
Java中既有单继承,⼜有多继承。对于java类来说只能有⼀个⽗类,对于接⼝来说可以同时继承多个接⼝
但⼀个业务逻辑包括多个数据库操作的时候,⽽且需要保证每个数据表操作都执⾏的成功进⾏下⼀个操作,这个时候可以使⽤事务
分为新⽣代和⽼年代,新⽣代默认占总空间的 1/3,⽼年代默认占 2/3。
新⽣代使⽤复制算法,有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占⽐是 8:1:1。
电机同步异步的区别当新⽣代中的 Eden 区内存不⾜时,就会触发 Minor GC,过程如下:
1、 在 Eden 区执⾏了第⼀次 GC 之后,存活的对象会被移动到其中⼀个 Survivor 分区;
2、 Eden 区再次 GC,这时会采⽤复制算法,将 Eden 和 from 区⼀起清理,存活的对象会被复制到 to 区;
3、 移动⼀次,对象年龄加 1,对象年龄⼤于⼀定阀值会直接移动到⽼年代
4、 Survivor 区相同年龄所有对象⼤⼩的总和 (Survivor 区内存⼤⼩ * 这个⽬标使⽤率)时,⼤于或等于该年龄的对象直接进⼊⽼年代。其中这个使⽤率通过 -XX:TargetSurvivorRatio 指定,默认为 50%
5、 Survivor 区内存不⾜会发⽣担保分配
6、 超过指定⼤⼩的对象可以直接进⼊⽼年代
Major GC,指的是⽼年代的垃圾清理,但并未到明确说明何时在进⾏Major GC
FullGC,整个堆的垃圾收集,触发条件:
1、 每次晋升到⽼年代的对象平均⼤⼩>⽼年代剩余空间
2、 MinorGC后存活的对象超过了⽼年代剩余空间
3、 元空间不⾜
4、 () 可能会引起
5、 CMS GC异常,promotion failed:MinorGC时,survivor空间放不下,对象只能放⼊⽼年代,⽽⽼年代也放不下造成;concurrent mode failure:GC时,同时有对象要放⼊⽼年代,⽽⽼年代空间不⾜造成
6、 堆内存分配很⼤的对象
当然可以。但是如果我们调⽤了Thread的run()⽅法,它的⾏为就会和普通的⽅法⼀样,会在当前线程中执⾏。为了在新的线程中执⾏我们的代码,必须使⽤Thread.start()⽅法。
1、 以字节为单位输⼊输出数据,字节流按照8位传输
2、 以字符为单位输⼊输出数据,字符流按照16位传输
JVM 的堆是运⾏时数据区,所有类的实例和数组都是在堆上分配内存。它在 JVM 启动的时候被创建。对象所占的堆内存是由⾃动内存管理系统也就是垃圾收集器回收。
堆内存是由存活和死亡的对象组成的。存活的对象是应⽤可以访问的,不会被垃圾回收。死亡的对象是应⽤不可访问尚且还没有被垃圾收集器回收掉的对象。⼀直到垃圾收集器把这些 对象回收掉之前,他们会⼀直占据堆内存空间。
双亲委托模型的重要⽤途是为了解决类载⼊过程中的安全性问题。
1、 假设有⼀个开发者⾃⼰编写了⼀个名为java.lang.Object的类,想借此欺骗JVM。现在他要使⽤⾃定义ClassLoader来加载⾃⼰编写的java.lang.Object类。
2、 然⽽幸运的是,双亲委托模型不会让他成功。因为JVM会优先在Bootstrap ClassLoader的路径下到java.lang.Object类,并载⼊它
Java的类加载是否⼀定遵循双亲委托模型?
1、 在实际开发中,我们可以通过⾃定义ClassLoader,并重写⽗类的loadClass⽅法,来打破这⼀机制。
2、 SPI就是打破了双亲委托机制的(SPI:服务提供发现)。
1
1、 top + H 指令出占⽤ CPU 最⾼的进程的 pid
2、 top -H -p
在该进程中到,哪些线程占⽤的 CPU 最⾼的线程,记录下 tid
3、 jstack -l
<,导出进程的线程栈信息到⽂本,导出出现异常的话,加上 -F 参数
4、 将 tid 转换为⼗六进制,在 中搜索,查到对应的线程代码执⾏栈,在代码中查占 CPU ⽐较⾼的原因。其中 tid 转⼗六进制,可以借助 Linux 的 printf "%x" tid 指令
我⽤上述⽅法查到过,jvm 多条线程疯狂 full gc 导致的CPU 100% 的问题和 JDK1.6 HashMap 并发 put 导致线程 CPU 100% 的问题
1
JVM 试图定义⼀种统⼀的内存模型,能将各种底层硬件以及操作系统的内存访问差异进⾏封装,使 Ja
va 程序在不同硬件以及操作系统上都能达到相同的并发效果。它分为⼯作内存和主内存,线程⽆法对主存储器直接进⾏操作,如果⼀个线程要和另外⼀个线程通信,那么只能通过主存进⾏交换。
1
Minor GC:发⽣在年轻代的 GC。Major GC:发⽣在⽼年代的 GC。Full GC:全堆垃圾回收。⽐如 Metaspace 区引起年轻代和⽼年代的回收。
1
运⾏时常量池主要回收的是废弃的常量。假如在常量池中存在字符串 "abc",如果当前没有任何 String 对象引⽤该字符串常量的话,就说明常量 "abc" 就是废弃常量,如果这时发⽣内存回收的话⽽且有必要的话,"abc" 就会被系统清理出常量池。
1
对象在堆内存的存储布局可分为对象头、实例数据和对齐填充。
对象头占 12B,包括对象标记和类型指针。对象标记存储对象⾃⾝的运⾏时数据,如哈希码、GC 分代年龄、锁标志、偏向线程 ID 等,这部分占 8B,称为 Mark Word。Mark Word 被设计为动态数据结构,以便在极⼩的空间存储更多数据,根据对象状态复⽤存储空间。
类型指针是对象指向它的类型元数据的指针,占 4B。JVM 通过该指针来确定对象是哪个类的实例。
实例数据是对象真正存储的有效信息,即本类对象的实例成员变量和所有可见的⽗类成员变量。存储顺序会受到虚拟机分配策略参数和字段在源码中定义顺序的影响。相同宽度的字段总是被分配到⼀起存放,在满⾜该前提条件的情况下⽗类中定义的变量会出现在⼦类之前。
对齐填充不是必然存在的,仅起占位符作⽤。虚拟机的⾃动内存管理系统要求任何对象的⼤⼩必须是 8B 的倍数,对象头已被设为 8B 的 1或 2 倍,如果对象实例数据部分没有对齐,需要对齐填充补全。
1
对象头区域此处存储的信息包括两部分:1、对象⾃⾝的运⾏时数据( MarkWord ),占8字节 存储 hashCode、GC 分代年龄、锁类型标记、偏向锁线程 ID 、 CAS 锁指向线程 LockRecord 的指针等, synconized 锁的机制与这个部分( markwork )密切相关,⽤markword 中最低的三位代表锁的状态,其中⼀位是偏向锁位,另外两位是普通锁位。2、对象类型指针( Class Pointer ),占4字节 对象指向它的类元数据的指针、 JVM 就是通过它来确定是哪个 Class 的实例。
实例数据区域 此处存储的是对象真正有效的信息,⽐如对象中所有字段的内容
对齐填充区域 JVM 的实现 HostSpot 规定对象的起始地址必须是 8 字节的整数倍,换句话来说,现在
64 位的 OS 往外读取数据的时候⼀次性读取 64bit 整数倍的数据,也就是 8 个字节,所以 HotSpot 为了⾼效读取对象,就做了"对齐",如果⼀个对象实际占的内存⼤⼩不是 8byte 的整数倍时,就"补位"到 8byte 的整数倍。所以对齐填充区域的⼤⼩不是固定的。
这套7000多页的Java⾯试题 PDF ⼤全,希望对⼤家有帮助哈~
1
1、 Parents Delegation Model,这⾥的 Parents 翻译成双亲有点不妥,类加载向上传递的过程中只有单亲;parents 更多的是多级向上的意思。
2、 除了顶层的启动类加载器,其他的类加载器在加载之前,都会委派给它的⽗加载器进⾏加载,⼀层层向上传递,直到所有⽗类加载器都⽆法加载,⾃⼰才会加载该类。
3、 双亲委派模型,更好地解决了各个类加载器协作时基础类的⼀致性问题,避免类的重复加载;防⽌核⼼API库被随意篡改。
JDK 9 之前
1、 启动类加载器(Bootstrp ClassLoader),加载 /lib/rt.jar、-Xbootclasspath
2、 扩展类加载器(Extension ClassLoader)sun.misc.Launcher$ExtClassLoader,加载 /lib/ext、dirs
3、 应⽤程序类加载器(Application ClassLoader,sun.misc.Launcher$AppClassLoader),加载 CLASSPTH、-classpath、-cp、Manifest
4、 ⾃定义类加载器
JDK 9 开始 Extension ClassLoader 被 Platform ClassLoader 取代,启动类加载器、平台类加载器、应⽤程序类加载器全都继承于jdk.internal.loader.BuiltinClassLoader
类加载代码逻辑
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // ⾸先,检查请求的类是否已经被加载过了 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c =
parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 如果⽗类加载器抛出ClassNotFoundException // 说明⽗类加载器⽆法完成加载请求 } if (c == null) { // 在⽗类加载器⽆法加载时 // 再调⽤本⾝的findClass⽅法来进⾏类加
载 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
1
CMS已经弃⽤。⽣活美好,时间有限,不建议再深⼊研究了。如果碰到问题,直接祭出回收过程即可。
1、 初始标记
2、 并发标记
3、 并发预清理
4、 并发可取消的预清理
ruby语言游戏引擎5、 重新标记
6、 并发清理
由于《深⼊理解java虚拟机》⼀书的流⾏,⾯试时省略3、4步⼀般也是没问题的。
当通过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的⼀部分⽤于创建堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC 是 JVM 内部的⼀个进程,回收⽆效对象的内存⽤于将来的分配。
2
JDK11 中加⼊的具有实验性质的低延迟垃圾收集器,⽬标是尽可能在不影响吞吐量的前提下,实现在任意堆内存⼤⼩都可以把停顿时间限制在 10ms 以内的低延迟。
基于 Region 内存布局,不设分代,使⽤了读屏障、染⾊指针和内存多重映射等技术实现可并发的标记-整理,以低延迟为⾸要⽬标。
ZGC 的 Region 具有动态性,是动态创建和销毁的,并且容量⼤⼩也是动态变化的。
2
STW并不会只发⽣在内存回收的时候。现在程序员这么卷,碰到⼏次safepoint的问题⼏率也是⽐较⼤的。
当发⽣GC时,⽤户线程必须全部停下来,才可以进⾏垃圾回收,这个状态我们可以认为JVM是安全的(safe),整个堆的状态是稳定的。
如果在GC前,有线程迟迟进⼊不了safepoint,那么整个JVM都在等待这个阻塞的线程,造成了整体GC的时间变长。
2
jps:
⽤来显⽰本地的 Java 进程,可以查看本地运⾏着⼏个 Java 程序,并显⽰他们的进程号。 命令格式:jps
jinfo:
易语言websocket教程运⾏环境参数:Java System 属性和 JVM 命令⾏参数,Java class path 等信息。 命令格式:jinfo 进程 pid
jstat:
监视虚拟机各种运⾏状态信息的命令⾏⼯具。 命令格式:jstat -gc 123 250 20
jstack:
可以观察到 JVM 中当前所有线程的运⾏情况和线程当前状态。 命令格式:jstack 进程 pid
jmap:
观察运⾏中的 JVM 物理内存的占⽤情况(如:产⽣哪些对象,及其数量)。 命令格式:jmap [option] pid
osu排名2
Concurrent mark sweep(CMS)收集器是⼀种年⽼代垃圾收集器,其最主要⽬标是获取最短垃圾回收停顿时间, 和其他年⽼代使⽤标记-整理算法不同,它使⽤多线程的标记-清除算法。最短的垃圾收集停顿时间可以为交互⽐较⾼的程序提⾼⽤户体验。CMS ⼯作机制相⽐其他的垃圾收集器来说更复杂。整个过程分为以下 4 个阶段:
初始标记
只是标记⼀下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的⼯作线程。
并发标记
进⾏ GC Roots 跟踪的过程,和⽤户线程⼀起⼯作,不需要暂停⼯作线程。
重新标记
为了修正在并发标记期间,因⽤户程序继续运⾏⽽导致标记产⽣变动的那⼀部分对象的标记记录,仍然需要暂停所有的⼯作线程。
并发清除
清除 GC Roots 不可达对象,和⽤户线程⼀起⼯作,不需要暂停⼯作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和⽤户现在⼀起并发⼯作, 所以总体上来看CMS 收集器的内存回收和⽤户线程是⼀起并发地执⾏。
2
不是。当新⽣代内存不够时,⽼年代分配担保。⽽⼤对象则是直接在⽼年代分配。
2
标记-清除算法
分为标记和清除阶段,⾸先从每个 GC Roots 出发依次标记有引⽤关系的对象,最后清除没有标记的对象。
执⾏效率不稳定,如果堆包含⼤量对象且⼤部分需要回收,必须进⾏⼤量标记清除,导致效率随对象数量增长⽽降低。
存在内存空间碎⽚化问题,会产⽣⼤量不连续的内存碎⽚,导致以后需要分配⼤对象时容易触发 Full GC。
标记-复制算法
为了解决内存碎⽚问题,将可⽤内存按容量划分为⼤⼩相等的两块,每次只使⽤其中⼀块。当使⽤的这块空间⽤完了,就将存活对象复制到另⼀块,再把已使⽤过的内存空间⼀次清理掉。主要⽤于进⾏新⽣代。
实现简单、运⾏⾼效,解决了内存碎⽚问题。代价是可⽤内存缩⼩为原来的⼀半,浪费空间。
HotSpot 把新⽣代划分为⼀块较⼤的 Eden 和两块较⼩的 Survivor,每次分配内存只使⽤ Eden 和其中⼀块 Survivor。垃圾收集时将Eden 和 Survivor 中仍然存活的对象⼀次性复制到另⼀块 Survivor 上,然后直接清理掉 Eden 和已⽤过的那块 Survivor。HotSpot 默认Eden 和 Survivor 的⼤⼩⽐例是 8:1,即每次新⽣代中可⽤空间为整个新⽣代的 90%。
标记-整理算法
标记-复制算法在对象存活率⾼时要进⾏较多复制操作,效率低。如果不想浪费空间,就需要有额外空间分配担保,应对被使⽤内存中所有对象都存活的极端情况,所以⽼年代⼀般不使⽤此算法。
⽼年代使⽤标记-整理算法,标记过程与标记-清除算法⼀样,但不直接清理可回收对象,⽽是让所有存活对象都向内存空间⼀端移动,然后清理掉边界以外的内存。
标记-清除与标记-整理的差异在于前者是⼀种⾮移动式算法⽽后者是移动式的。如果移动存活对象,尤其是在⽼年代这种每次回收都有⼤量对象存活的区域,是⼀种极为负重的操作,⽽且移动必须全程暂停⽤户线程。如果不移动对象就会导致空间碎⽚问题,只能依赖更复杂的内存分配器和访问器解决。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论