内容分类 | 详情 |
---|---|
Java高频面试题 | 汇总入口 |
JVM | JVM面试题 |
并发 | 并发面试题 |
Spring | Spring面试题 |
分布式 | 分布式面试题 |
SpringBoot | SpringBoot面试题 |
SpringCloud | SpringCloud面试题 |
Dubbo | Dubbo面试题 |
MySQL | MySQL面试题 |
Mybatis | Mybatis面试题 |
Redis | Redis面试题 |
RocketMQ | RocketMQ面试题 |
算法 | 算法面试题 |
遇到的问题 | 遇到的问题 |
面试官的其他问题 | 面试官的其他问题 |
Git | Git面试题 |
JDK: Java Development Kit,Java开发工具包,是一个编写Java应用程序的开发环境。
JDK是整个Java的核心,包括了JRE(Java运行环境)与一些Java开发工具(例如:jconsole、javac、java、javadoc、native2ascii、jar等)。JDK=JRE+Java开发工具(编译器、调试器等),javac命令调用JDk中的编译器,将.java文件编译成二进制流的.class文件。
JVM(Java Virtual Machine),即Java虚拟机,运行在操作系统之上,存在于内存中,与内存打交道,与硬件没有直接交互,是Java语言实现跨平台的核心。JVM主要负责运行Java编译器编译后的字节码文件(*.class文件)。JVM在执行字节码时,把字节码解释成具体平台上的机器码执行。JVM自己无法执行,必须要联合JRE中的Java基础&核心类库才能使用。
JRE(Java Runtime Environment),即Java运行环境,支持Java程序运行的标准环境,包括了JVM(Java虚拟机)的标准实现以及Java基础&核心类库。JRE=JVM+Java基础&核心类库。
JRE=JVM+Java基础&核心类库。
JDK=JRE+Java开发工具(编译器、调试器等)。
程序计数器:当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址,线程私有。
Java虚拟栈:存放基本数据类型、对象的引用、方法出口等,线程私有。
Native方法栈:和虚拟栈相似,只不过它服务于Native方法,线程私有。
Java堆:java内存最大的一块,所有对象实例、数组都存放在java堆,GC回收的地方,线程共享。
方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。回收目标主要是常量池的回收和类型的卸载,各线程共享
原因:Heap内存溢出,意味着Young和Old generation的内存不够。
解决:调整java启动参数 -Xms -Xmx 来增加Heap内存。
ms表示初始堆内存大小,mx表示最大堆内存大小
原因:主要是反射产生大量的类不断加载,导致永久代被占满。
解决:调整-XX:PermSize= -XX:MaxPermSize= 两个参数来增大PermGen内存。一般情况下,这两个参数不要手动设置,只要设置-Xmx足够大即可,JVM会自行选择合适的PermGen大小。
当前线程申请的栈深度大域虚拟机允许的栈深度,线程栈满了,从循环或者递归那里找问题。
原因:当前线程申请的栈深度大域虚拟机允许的栈深度
解决:优化程序设计,减少方法调用层次;调整-Xss参数增加线程栈大小。
原因:虚拟机栈太小
解决:涉及到的参数:-Xss2m -> 调大
原因:栈内存不足以创建新的线程
解决:
原因:这个错误比较少见(试着new一个长度1亿的数组看看),同样是由于Heap空间不足。如果需要new一个如此之大的数组,程序逻辑多半是不合理的。
解决:修改程序逻辑吧。或者也可以通过-Xmx来增大堆内存。
原因:当使用-XX:+UseParallelGC或-XX:+UseConcMarkSweepGC收集器时,在上述情况下会报错,在HotSpot GC Turning文档上有说明:
The parallel(concurrent) collector will throw an OutOfMemoryError if too much time is being spent in garbage collection: if more than 98% of the total time is spent in garbage collection and less than 2% of the heap is recovered, an OutOfMemoryError will be thrown.
解决:
一是需要进行GC turning,二是需要优化程序逻辑。
新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。
老年代 GC(Major GC):指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。
共享内存区 = 持久带 + 堆
持久带 = 方法区 + 其他
Java堆 = 老年代 + 新生代
新生代 = Eden + S0 + S1
默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ,可以通过参数 –XX:NewRatio 配置。
默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定)
Survivor区中的对象被复制次数为15(对应虚拟机参数 -XX:+MaxTenuringThreshold)
如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC.老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多,所以需要分为Eden和Survivor。
Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。
设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)
Java堆 = 老年代 + 新生代
新生代 = Eden + S0 + S1
当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对象,则会转移到 Survivor区。
大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年态;
如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次Minor GC,年龄+1,若年龄超过一定限制(15),则被晋升到老年代。即长期存活的对象进入老年代。
老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 – 包括年轻代和年老代。
Major GC 发生在老年代的GC,清理老年区,经常会伴随至少一次Minor GC,比Minor GC慢10倍以上。
Serial收集器: 单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。
ParNew收集器: Serial收集器的多线程版本,也需要stop the world,复制算法。
Parallel Scavenge收集器: 新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量。如果虚拟机总共运行100分钟,其中垃圾花掉1分钟,吞吐量就是99%。
Serial Old收集器: 是Serial收集器的老年代版本,单线程收集器,使用标记整理算法。
Parallel Old收集器: 是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。
CMS(Concurrent Mark Sweep) 收集器: 是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片。
G1收集器: 标记整理算法实现,运作流程主要包括以下:初始标记,并发标记,最终标记,筛选标记。不会产生空间碎片,可以精确地控制停顿。
JVM: Java Virtual Machine
JMM: Java Main Memory
JMM:从抽象的角度来看,定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。
volatile其含义是 ‘易变的’
类加载器的作用:根据文件的全限定名将JDK编译的class文件,加载到JVM中,转化为class对象。
java
-Xmx3550m
-Xms3550m
-Xmn2g
-Xss128k
-XX:MaxPermSize=16m
-XX:NewRatio=4
-XX:SurvivorRatio=4
-XX:MaxTenuringThreshold=0
-Xmx3550m: 最大堆大小为3550m。
-Xms3550m: 设置初始堆大小为3550m。
-Xmn2g: 设置新生代大小为2g。
-Xss128k: 每个线程的堆栈大小为128k。
-XX:MaxPermSize: 设置持久代大小为16m
-XX:NewRatio=4: 设置新生代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。
-XX:SurvivorRatio=4: 设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxTenuringThreshold=0: 设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。
双亲委派导致的问题:在双亲委派模型中,由父加载类加载的类
这条安全异常是由Java类加载的“双亲委派模型”所导致的。在双亲委派模型中,由父加载类加载的类,下层加载器是不能加载的。本例中最高层加载器BootstrapClassLoader加载了classpath路径下所定义的java.包内的类,而java.lang包就不能由BootstrapClassLoader的下层加载器AppClassLoader加载了。这也是java安全机制中对于恶意代码所采取的防护措施。