关于java:Java开发之虚拟机八股文分享

51次阅读

共计 5083 个字符,预计需要花费 13 分钟才能阅读完成。

简述 JVM 内存模型
线程公有的运行时数据区: 程序计数器、Java 虚拟机栈、本地办法栈。
线程共享的运行时数据区:Java 堆、办法区。
简述程序计数器
程序计数器示意以后线程所执行的字节码的行号指示器。
程序计数器不会产生 StackOverflowError 和 OutOfMemoryError。
简述虚拟机栈
Java 虚拟机栈用来形容 Java 办法执行的内存模型。线程创立时就会调配一个栈空间,线程完结后栈空间被回收。
栈中元素用于反对虚拟机进行办法调用,每个 java 培训办法在执行时都会创立一个栈帧存储办法的局部变量表、操作栈、动静链接和返回地址等信息。

虚拟机栈会产生两类异样:
StackOverflowError:线程申请的栈深度大于虚拟机容许的深度抛出。
OutOfMemoryError:如果 JVM 栈容量能够动静扩大,虚拟机栈占用内存超出抛出。
简述本地办法栈
本地办法栈与虚拟机栈作用类似,不同的是虚拟机栈为虚拟机执行 Java 办法服务,本地办法栈为本地办法服务。能够将虚拟机栈看作一般的 java 函数对应的内存模型,本地办法栈看作由 native 关键词润饰的函数对应的内存模型。
本地办法栈会产生两类异样:
StackOverflowError:线程申请的栈深度大于虚拟机容许的深度抛出。
OutOfMemoryError:如果 JVM 栈容量能够动静扩大,虚拟机栈占用内存超出抛出。
简述 JVM 中的堆
堆次要作用是寄存对象实例,Java 里简直所有对象实例都在堆分配内存,堆也是内存治理中最大的一块。Java 的垃圾回收次要就是针对堆这一区域进行。可通过 -Xms 和 -Xmx 设置堆的最小和最大容量。
堆会抛出 OutOfMemoryError 异样。
简述办法区
办法区用于存储被虚拟机加载的类信息、常量、动态变量等数据。
JDK6 之前应用永恒代实现办法区,容易内存溢出。JDK7 把放在永恒代的字符串常量池、动态变量等移出,JDK8 中摈弃永恒代,改用在本地内存中实现的元空间来实现办法区,把 JDK 7 中永恒代内容移到元空间。
办法区会抛出 OutOfMemoryError 异样。
简述运行时常量池
运行时常量池寄存常量池表,用于寄存编译器生成的各种字面量与符号援用。个别除了保留 Class 文件中形容的符号援用外,还会把符号援用翻译的间接援用也存储在运行时常量池。除此之外,也会寄存字符串根本类型。
JDK8 之前,放在办法区,大小受限于办法区。JDK8 将运行时常量池寄存堆中。
简述间接内存
间接内存也称为堆外内存,就是把内存对象调配在 JVM 堆外的内存区域。这部分内存不是虚拟机治理,而是由操作系统来治理。Java 通过通过 DriectByteBuffer 对其进行操作,防止了在 Java 堆和 Native 堆来回复制数据。
简述 java 创建对象的过程

  1. 查看该指令的参数是否在常量池中定位到一个类的符号援用,并查看援用代表的类是否已被加载、解析和初始化,如果没有就先执行类加载。
  2. 通过查看通过后虚拟机将为新生对象分配内存。
  3. 实现内存调配后虚拟机将成员变量设为零值
  4. 设置对象头,包含哈希码、GC 信息、锁信息、对象所属类的类元信息等。
  5. 执行 init 办法,初始化成员变量,执行实例化代码块,调用类的构造方法,并把堆内对象的首地址赋值给援用变量。

简述 JVM 给对象分配内存的策略

  1. 指针碰撞:这种形式在内存中放一个指针作为分界指示器将应用过的内存放在一边,闲暇的放在另一边,通过指针移动实现调配。
  2. 闲暇列表:对于 Java 堆内存不规整的状况,虚拟机必须保护一个列表记录哪些内存可用,在调配时从列表中找到一块足够大的空间划分给对象并更新列表记录。

java 对象内存调配是如何保障线程平安的

  1. 对分配内存空间采纳 CAS 机制,配合失败重试的形式保障更新操作的原子性。该形式效率低。
  2. 每个线程在 Java 堆中事后调配一小块内存,而后再给对象分配内存的时候,间接在本人这块 ” 公有 ” 内存中调配。个别采纳这种策略。

简述对象的内存布局
对象在堆内存的存储布局可分为对象头、实例数据和对齐填充。
对象头次要蕴含两局部数据:MarkWord、类型指针。MarkWord 用于存储哈希码(HashCode)、GC 分代年龄、锁状态标记位、线程持有的锁、偏差线程 ID 等信息。类型指针即对象指向他的类元数据指针,如果对象是一个 Java 数组,会有一块用于记录数组长度的数据,
实例数据存储代码中所定义的各种类型的字段信息。
对齐填充起占位作用。HotSpot 虚拟机要求对象的起始地址必须是 8 的整数倍,因而须要对齐填充。
如何判断对象是否是垃圾
援用计数法:设置援用计数器,对象被援用计数器加 1,援用生效时计数器减 1,如果计数器为 0 则被标记为垃圾。会存在对象间循环援用的问题,个别不应用这种办法。
可达性剖析:通过 GC Roots 的根对象作为起始节点,从这些节点开始,依据援用关系向下搜寻,如果某个对象没有被搜到,则会被标记为垃圾。可作为 GC Roots 的对象包含虚拟机栈和本地办法栈中援用的对象、类动态属性援用的对象、常量援用的对象。
简述 java 的援用类型
强援用:被强援用关联的对象不会被回收。个别采纳 new 办法创立强援用。
软援用:被软援用关联的对象只有在内存不够的状况下才会被回收。个别采纳 SoftReference 类来创立软援用。

弱援用:垃圾收集器碰到即回收,也就是说它只能存活到下一次垃圾回收产生之前。个别采纳 WeakReference 类来创立弱援用。
虚援用:无奈通过该援用获取对象。惟一目标就是为了能在对象被回收时收到一个零碎告诉。虚援用必须与援用队列联结应用。
简述标记革除算法、标记整顿算法和标记复制算法
标记革除算法:先标记需革除的对象,之后对立回收。这种办法效率不高,会产生大量不间断的碎片。
标记整顿算法:先标记存活对象,而后让所有存活对象向一端挪动,之后清理端边界以外的内存
标记复制算法:将可用内存按容量划分为大小相等的两块,每次只应用其中一块。当应用的这块空间用完了,就将存活对象复制到另一块,再把已应用过的内存空间一次清理掉。
简述分代收集算法
依据对象存活周期将内存划分为几块,不同块采纳适当的收集算法。个别将堆分为新生代和老年代,对这两块采纳不同的算法。新生代应用:标记复制算法
老年代应用:标记革除或者标记整顿算法
简述 Serial 垃圾收集器
单线程串行收集器。垃圾回收的时候,必须暂停其余所有线程。新生代应用标记复制算法,老年代应用标记整顿算法。简略高效。
简述 ParNew 垃圾收集器
能够看作 Serial 垃圾收集器的多线程版本,新生代应用标记复制算法,老年代应用标记整顿算法。
简述 Parallel Scavenge 垃圾收集器
重视吞吐量,即 cpu 运行代码工夫 /cpu 耗时总工夫(cpu 运行代码工夫 + 垃圾回收工夫)。新生代应用标记复制算法,老年代应用标记整顿算法。
简述 CMS 垃圾收集器
重视最短时间进展。CMS 垃圾收集器为最早提出的并发收集器,垃圾收集线程与用户线程同时工作。采纳标记革除算法。该收集器分为初始标记、并发标记、并发预清理、并发革除、并发重置这么几个步骤。
初始标记:暂停其余线程 (stop the world),标记与 GC roots 间接关联的对象。并发标记:可达性剖析过程(程序不会进展)。
并发预清理:查找执行并发标记阶段从年老代降职到老年代的对象,从新标记,暂停虚拟机(stop the world)扫描 CMS 堆中残余对象。
并发革除:清理垃圾对象,(程序不会进展)。
并发重置,重置 CMS 收集器的数据结构。
简述 G1 垃圾收集器
和之前收集器不同,该垃圾收集器把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间能够独自进行垃圾回收。
初始标记:标记与 GC roots 间接关联的对象。
并发标记:可达性剖析。
最终标记,对并发标记过程中,用户线程批改的对象再次标记一下。
筛选回收:对各个 Region 的回收价值和老本进行排序,而后依据用户所冀望的 GC 进展工夫制订回收打算并回收。
简述 Minor GC
Minor GC 指产生在新生代的垃圾收集,因为 Java 对象大多存活工夫短,所以 Minor GC 十分频繁,个别回收速度也比拟快。
简述 Full GC
Full GC 是清理整个堆空间—包含年老代和永恒代。调用 System.gc(), 老年代空间有余,空间调配担保失败,永生代空间有余会产生 full gc。
常见内存调配策略
大多数状况下对象在新生代 Eden 区调配,当 Eden 没有足够空间时将发动一次 Minor GC。
大对象须要大量间断内存空间,间接进入老年代区调配。
如果经验过第一次 Minor GC 依然存活且能被 Survivor 包容,该对象就会被挪动到 Survivor 中并将年龄设置为 1,并且每熬过一次 Minor GC 年龄就加 1,当减少到肯定水平(默认 15)就会被降职到老年代。
如果在 Survivor 中雷同年龄所有对象大小的总和大于 Survivor 的一半,年龄不小于该年龄的对象就能够间接进入老年代。
空间调配担保。MinorGC 前虚拟机必须查看老年代最大可用间断空间是否大于新生代对象总空间,如果满足则阐明这次 Minor GC 确定平安。如果不,JVM 会查看 HandlePromotionFailure 参数是否容许担保失败,如果容许会持续查看老年代最大可用间断空间是否大于历次降职老年代对象的均匀大小,如果满足将 Minor GC,否则改成一次 FullGC。
简述 JVM 类加载过程
加载:

  1. 通过全类名获取类的二进制字节流.
  2. 将类的动态存储构造转化为办法区的运行时数据结构。
  3. 在内存中生成类的 Class 对象,作为办法区数据的入口。

验证:对文件格式,元数据,字节码,符号援用等验证正确性。
筹备:在办法区内为类变量分配内存并设置为 0 值。
解析:将符号援用转化为间接援用。
初始化:执行类结构器 clinit 办法,真正初始化。
简述 JVM 中的类加载器
BootstrapClassLoader 启动类加载器:加载 /lib 下的 jar 包和类。C++ 编写。
ExtensionClassLoader 扩大类加载器:/lib/ext 目录下的 jar 包和类。java 编写。
AppClassLoader 利用类加载器,加载以后 classPath 下的 jar 包和类。java 编写。
简述双亲委派机制
一个类加载器收到类加载申请之后,首先判断以后类是否被加载过。曾经被加载的类会间接返回,如果没有被加载,首先将类加载申请转发给父类加载器,始终转发到启动类加载器,只有当父类加载器无奈实现时才尝试本人加载。
加载类程序:BootstrapClassLoader->ExtensionClassLoader->AppClassLoader->CustomClassLoader 查看类是否加载程序:CustomClassLoader->AppClassLoader->ExtensionClassLoader->BootstrapClassLoader
双亲委派机制的长处

  1. 防止类的反复加载。雷同的类被不同的类加载器加载会产生不同的类,双亲委派保障了 java 程序的稳固运行。
  2. 保障外围 API 不被批改。

如何毁坏双亲委派机制
重载 loadClass()办法,即自定义类加载器。
如何构建自定义类加载器

  1. 新建自定义类继承自 java.lang.ClassLoader
  2. 重写 findClass、loadClass、defineClass 办法

JVM 常见调优参数
• -Xms 初始堆大小
• -Xmx 最大堆大小
• -XX:NewSize 年老代大小
• -XX:MaxNewSize 年老代最大值
• -XX:PermSize 永生代初始值
• -XX:MaxPermSize 永生代最大值
• -XX:NewRatio 新生代与老年代的比例

调用 system.gc()肯定会产生垃圾收集吗?为什么?
调用 System.gc()的时候,其实并不会马上进行垃圾回收, 只会把这次 gc 申请记录下来。需配合 System.runFinalization()才会进行真正回收
动态变量存储地位
在 1.8 以前,动态成员变量存在办法区,在 1.8 后,因为 JDK8 勾销永生代,动态变量存储到了堆中。
内存溢出和内存透露
内存溢出:程序在申请内存时,此时已用内存过多,没有足够的残余内存空间供其应用。
内存透露:程序在申请内存后,不能齐全开释已申请的内存空间。
垃圾收集器品种:
串行收集器:Serial,Serial Old 并行收集器:Parallel Scavenge,Parallel Old 并发收集器:CMS,G1
作者:后端技术小牛说

正文完
 0