JVM(面试题)

目录

1、Java 的内存划分?

2、什么是 Java 虚拟机?

3、如何判断一个对象应该被回收?

4、GC 触发的条件?

5、可以作为 GCRoots 的对象有哪些?

6、JVM 中一次完整的 GC 流程是怎样的,对象如何晋升到老年代?

7、双亲委派模型?

8、为什么需要双亲委派模型?

9、怎么打破双亲委派模型?

10、导致 Full GC 一般有哪些情况?

11、Minor GC,Full GC 触发条件?

12、JVM 性能调优?

13、Java 内存模型?

14、Java 中堆和栈有什么区别?

15、常见的垃圾回收算法有哪些?简述其原理?

16、解释栈(stack)、堆(heap)和方法区(method area)的用法?

17、什么是类的加载?

18、类加载器有哪些?

19、Java 对象创建过程?

20、Java 中类的生命周期使什么?

21、都有哪些垃圾回收器?

22、JVM 调优命令?

23、JVM 调优工具?

24、描述一下 JVM 加载 class 文件的原理机制?

25、GC 是什么?为什么要有 GC?

26、垃圾回收器的基本原理是什么?

27、Java 中的引用类型有几种?


1、Java 的内存划分?

         程序计数器(PC,Program Counter Register)。在 JVM 规范中,每个线程都有它自己的程序计数器,并且任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的 Java 方法的 JVM 指令地址;或者,如果是在执行本地方法,则是未指定值(undefined)。(唯一不会抛出 OutOfMemoryError)

        第二,Java 虚拟机栈(Java Virtual Machine Stack),早期也叫 Java 栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用。 前面谈程序计数器时,提到了当前方法;同理,在一个时间点,对应的只会有一个活动的栈帧,通常叫作当前帧,方法所在的类叫作当前类。如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,成为新的当前帧,一直到它返回结果或者执行结束。JVM 直接对Java 栈的操作只有两个,就是对栈帧的压栈和出栈。栈帧中存储着局部变量表、操作数(operand)栈、动态链接、方法正常退出或者异常退出的定义等。

        第三,堆(Heap),它是 Java 内存管理的核心区域,用来放置 Java 对象实例,几乎所有创建的 Java 对象实例都是被直接分配在堆上。堆被所有的线程共享,在虚拟机启动时,我们指定的“Xmx”之类参数就是用来指定最大堆空间等指标。 ( 编译器通过逃逸分析,确定对象是在栈上分配还是在堆上分配) 理所当然,堆也是垃圾收集器重点照顾的区域,所以堆内空间还会被不同的垃圾收集器进行进一步的细分,最有名的就是新生代、老年代的划分。

         第四,方法区(Method Area)。这也是所有线程共享的一块内存区域,用于存储所谓的元(Meta)数据,例如类结构信息,以及对应的运行时常量池、字段、方法代码等。由于早期的 Hotspot JVM 实现,很多人习惯于将方法区称为永久代(Permanent Generation)。Oracle JDK 8 中将永久代移除,同时增加了元数据区(Metaspace)。

         第五,运行时常量池(Run-Time Constant Pool),这是方法区的一部分。如果仔细分析过反编译的类文件结构,你能看到版本号、字段、方法、超类、接口等各种信息,还有一项信息就是常量池。Java 的常量池可以存放各种常量信息,不管是编译期生成的各种字面量,还是需要在运行时决定的符号引用,所以它比一般语言的符号表存储的信息更加宽泛。第六,本地方法栈(Native Method Stack)。它和 Java 虚拟机栈是非常相似的,支持对本地方法的调用,也是每个线程都会创建一个。在 Oracle Hotspot JVM 中,本地方法栈和 Java虚拟机栈是在同一块儿区域,这完全取决于技术实现的决定,并未在规范中强制。

2、什么是 Java 虚拟机?

        为什么 Java 被称作是无关平台的编程语言? Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件。 Java 被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java 虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。

3、如何判断一个对象应该被回收?

        1)在 Java 中采取了 可达性分析法 通过一系列的“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。

        2)虚拟机栈中引用的对象、方法区类静态属性引用的对象、方法区常量池引用的对象、本地方法栈 JNI 引用的对象

4、GC 触发的条件?

        (1)程序调用 System.gc 时可以触发;

      (2)系统自身来决定 GC 触发的时机

5、可以作为 GCRoots 的对象有哪些?

         虚拟机栈中引用的对象方法区中类静态属性引用的对象方法区中常量引用的对象本地方法栈中引用的对象

6、JVM 中一次完整的 GC 流程是怎样的,对象如何晋升到老年代?

        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 倍以上。

7、双亲委派模型?

         双亲委派模型工作过程是: 如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即 ClassNotFoundException),子加载器才会尝试自己去加载。

8、为什么需要双亲委派模型?

        防止内存中出现多份同样的字节码

9、怎么打破双亲委派模型?

        打破双亲委派机制则不仅要继承 ClassLoader 类,还要重写 loadClass 和 findClass 方法。

10、导致 Full GC 一般有哪些情况?

        1).新生代设置过小 一是新生代 GC 次数非常频繁,增大系统消耗;二是导致大对象直接进入旧生代,占据了 旧生代剩余空间,诱发 Full GC

        2). 新生代设置过大 一是新生代设置过大会导致旧生代过小(堆总量一定),从而诱发 Full GC;二是新生代 GC 耗时大幅度增加

        3). Survivor 设置过小 导致对象从 eden 直接到达旧生代

        4). Survivor 设置过大导致 eden 过小,增加了 GC 频率 一般说来新生代占整个堆 1/3 比较合适GC 策略的设置方式 1). 吞吐量优先 可由-XX:GCTimeRatio=n 来设置2). 暂停时间优先 可由-XX:MaxGCPauseRatio=n 来设置

11、Minor GC,Full GC 触发条件?

        Minor GC 触发条件:当 Eden 区满时,触发 Minor GC。Full GC 触发条件:

        (1)调用 System.gc 时,系统建议执行 Full GC,但是不必然执行

        (2)老年代空间不足

        (3)方法区空间不足

        (4)通过 Minor GC 后进入老年代的平均大小大于老年代的可用内存

        (5)由 Eden 区、From Space 区向 To Sp3ace 区复制时,对象大小大于 To Space 可存,则把 该对象转存到老年代,且老年代的可用内存小于该对象大小

12、JVM 性能调优?

        1、设定堆内存大小 -Xmx:堆内存最大限制。

        2、设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代-XX:NewSize:新生代大小 -XX:NewRatio 新生代和老生代占比 -XX:SurvivorRatio:伊甸园空间和幸存者空间的占比3、设定垃圾回收器 年轻代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC

13、Java 内存模型?

        Java 内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步。JMM 内部的实现通常是依赖于所谓的内存屏障,通过禁止某些重排序的方式,提供内存可见性保证,也就是实现了各种 happen-before 规则。 与 JVM 内存模型不同。 Java 内存模型即 Java Memory Model,简称 JMM。JMM 定义了 Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM 是整个计算机虚拟模型,所以 JMM 是隶属于 JVM 的。Java 内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步。 Java 线程之间的通信采用的是过共享内存模型,这里提到的共享内存模型指的就是 Java 内存模型(简称 JMM),JMM 决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。

14、Java 中堆和栈有什么区别?

        最主要的区别就是栈内存用来存储局部变量和方法调用。 而堆内存用来存储 Java 中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。 独有还是共享 栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。 而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。异常错误 如果栈内存没有可用的空间存储方法调用和局部变量,JVM 会抛出java.lang.StackOverFlowError。 而如果是堆内存没有可用的空间存储生成的对象,JVM 会抛出 java.lang.OutOfMemoryError。空间大小 栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满。如果递归没有及时跳出,很可能发生 StackOverFlowError 问题。

15、常见的垃圾回收算法有哪些?简述其原理?

        GC 最基础的算法有三种: 标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。 标记 -清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。 复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。 标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存分代收集算法,“分代收集”(Generational Collection)算法 ,把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

16、解释栈(stack)、堆(heap)和方法区(method area)的用法?

        通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用 JVM 中的栈空间;而通过 new 关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点可以分为 Eden、Survivor(又可分为 From Survivor 和 ToSurvivor)、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被 JVM 加载的类信息、常量、静态变量、JIT 编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的 100、”hello”和常量都是放在常量池中,常量池是方法区的一部分,。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过 JVM的启动参数来进行调整,栈空间用光了会引发 StackOverflowError,而堆和常量池空间不足则会引发 OutOfMemoryError。

17、什么是类的加载?

        类的加载指的是将类的.class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的 Class 对象,Class 对象封装了类在方法区内的数据结构,并且向 Java 程序员提供了访问方法区内的数据结构的接口。

18、类加载器有哪些?

        启动类加载器:Bootstrap ClassLoader,负责加载存放在 JDK\jre\lib(JDK 代表 JDK 的安装目录,下同)下,或被-Xbootclasspath 参数指定的路径中的,并且能被虚拟机识别的类库扩展类加载器:Extension ClassLoader,该加载器由 sun.misc.Launcher$ExtClassLoader 实现,它负责加载 DK\jre\lib\ext 目录中,或者由 java.ext.dirs 系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。 应用程序类加载器:Application ClassLoader,该类加载器由 sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器

19、Java 对象创建过程?

        1.JVM 遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符号引用。然后加载这个类(类加载过程在后边讲)

        2.为对象分配内存。一种办法“指针碰撞”、一种办法“空闲列表”,最终常用的办法“本地线程缓冲分配(TLAB)” 3.将除对象头外的对象内存空间初始化为 0 4.对对象头进行必要设置

20、Java 中类的生命周期使什么?

        1、加载,查找并加载类的二进制数据,在 Java 堆中也创建一个 java.lang.Class 类的对象

        2、连接,连接又包含三块内容:验证、准备、初始化。 1)验证,文件格式、元数据、字节码、符号引用验证; 2)准备,为类的静态变量分配内存,并将其初始化为默认值; 3)解析,把类中的符号引用转换为直接引用

        3、初始化,为类的静态变量赋予正确的初始值

        4、使用,new 出对象程序中使用

        5、卸载,执行垃圾回收

21、都有哪些垃圾回收器?

        Serial 收集器,串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿, 只使用一个线程去回收。 ParNew 收集器,ParNew 收集器其实就是 Serial 收集器的多线程版本。 Parallel 收集器,Parallel Scavenge 收集器类似 ParNew 收集器,Parallel 收集器更关注系统的 吞吐量。 Parallel Old 收集器,Parallel Old 是 Parallel Scavenge 收集器的老年代版本,使用多线程和“标 记-整理”算法 CMS 收集器,CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标 的收集器。G1 收集器,G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征

22、JVM 调优命令?

         Sun JDK 监控和故障处理命令有 jps jstat jmap jhat jstack jinfo

        1、jps,JVM Process Status Tool,显示指定系统内所有的 HotSpot 虚拟机进程。

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

        3、jmap,JVM Memory Map 命令用于生成 heap dump 文件

        4、jhat,JVM Heap Analysis Tool 命令是与 jmap 搭配使用,用来分析 jmap 生成的 dump,jhat内置了一个微型的 HTTP/HTML 服务器,生成 dump 的分析结果后,可以在浏览器中查看

        5、jstack,用于生成 java 虚拟机当前时刻的线程快照。

        6、jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。

23、JVM 调优工具?

        常用调优工具分为两类,jdk 自带监控工具:jconsole 和 jvisualvm,第三方有:MAT(MemoryAnalyzerTool)、GChisto。 1、jconsole,Java Monitoring and Management Console 是从 java5 开始,在 JDK 中自带的 java监控和管理控制台,用于对 JVM 中内存,线程和类等的监控2、jvisualvm,jdk 自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC 变化等。 3、MAT,Memory Analyzer Tool,一个基于 Eclipse 的内存分析工具,是一个快速、功能丰富的 Java heap 分析工具,它可以帮助我们查找内存泄漏和减少内存消耗4、GChisto,一款专业分析 gc 日志的工具

24、描述一下 JVM 加载 class 文件的原理机制?

        JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java 中的类加载器是一个重要的 Java 运行时系统组件,它负责在运行时查找和装入类文件中的类。类的加载是指把类的.class 文件中的数据读入到内存中,通常是创建一个字节数组读入.class 文件

25、GC 是什么?为什么要有 GC?

        GC 是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会 导致程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的, Java 语言没有提供释放已分配内存的显示操作方法。

26、垃圾回收器的基本原理是什么?

        对于 GC 来说,当程序员创建对象时,GC 就开始监控这个对象的地址、大小以及使用情况。通常,GC 采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当 GC 确定一些对象为"不可达"时,GC 就有责任回收这些内存空间。可以。程序员可以手动执行 System.gc(),通知 GC 运行,但是 Java 语言规范并不保证 GC 一定会执行。

27、Java 中的引用类型有几种?

        1、强引用 如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为 null,这样一来的话,JVM 在合适的时间就会回收该对象。

        2、软引用 在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收;只有在内存空间不足时,软引用才会被垃圾回收器回收

        3、弱引用 具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象

        4、虚引用 顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。 虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失忆机器

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值