十三、垃圾回收器
[toc]
13. 垃圾回收器13.1. GC 分类与性能指标13.1.1. 垃圾回收器概述垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商、不同版本的 JVM 来实现。
由于 JDK 的版本处于高速迭代过程中,因此 Java 发展至今已经衍生了众多的 GC 版本。
从不同角度分析垃圾收集器,可以将 GC 分为不同的类型。
13.1.2. 垃圾收集器分类按线程数分,可以分为串行垃圾回收器和并行垃圾回收器。
串行回收指的是在同一时间段内只允许有一个 CPU 用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集工作结束。
在诸如单 CPU 处理器或者较小的应用内存等硬件平台不是特别优越的场合,串行回收器的性能表现可以超过并行回收器和并发回收器。所以,串行回收默认被应用在客户端的 Client 模式下的 JVM 中
在并发能力比较强的 CPU 上,并行回收器产生的停顿时间要短于串行回收器。
和串行回收相反,并行收集可以运用多个 CPU 同时执行垃圾回收,因此提升了应用的吞吐量,不过并行回收仍然与串行回收一样,采用独占式,使用了“Stop-the-World”机制。
按照工作模式 ...
十二、垃圾回收相关概念
[toc]
12. 垃圾回收相关概念12.1. System.gc()的理解在默认情况下,通过 system.gc()或者 Runtime.getRuntime().gc() 的调用,会显式触发 Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。
然而 System.gc() 调用附带一个免责声明,无法保证对垃圾收集器的调用。(不能确保立即生效)
JVM 实现者可以通过 System.gc() 调用来决定 JVM 的 GC 行为。而一般情况下,垃圾回收应该是自动进行的,无须手动触发,否则就太过于麻烦了。在一些特殊情况下,如我们正在编写一个性能基准,我们可以在运行之间调用 System.gc()
案例一:
123456789101112131415public class SystemGCTest { public static void main(String[] args) { new SystemGCTest(); System.gc();// 提醒JVM的垃圾回收器执行gc,但是不确定是否马上执行gc ...
十一、垃圾回收相关算法
12. 垃圾回收相关算法对象存活判断
在堆里存放着几乎所有的 Java 对象实例,在 GC 执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为己经死亡的对象,GC 才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段。
那么在 JVM 中究竟是如何标记一个死亡对象呢?简单来说,当一个对象已经不再被任何的存活对象继续引用时,就可以宣判为已经死亡。
判断对象存活一般有两种方式:引用计数算法和可达性分析算法。
12.1. 标记阶段:引用计数算法12.1.1 什么是引用计数算法引用计数算法(Reference Counting)比较简单,对每个对象保存一个整型的引用计数器属性。用于记录对象被引用的情况。
对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加 1;当引用失效时,引用计数器就减 1。只要对象 A 的引用计数器的值为 0,即表示对象 A 不可能再被使用,可进行回收。
优点:实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性。
缺点:
它需要单独的字段存储计数器,这样的做法增加了存储空间的开 ...
十、StringTable
[toc]
10. StringTable10.1. String 的基本特性
String:字符串,使用一对””引起来表示
String 声明为 final 的,不可被继承
String 实现了 Serializable 接口:表示字符串是支持序列化的。
String 实现了 Comparable 接口:表示 string 可以比较大小
String 在 jdk8 及以前内部定义了 final char[] value 用于存储字符串数据。JDK9 时改为 byte[]
10.1.1. String 在 jdk9 中存储结构变更官网地址:JEP 254: Compact Strings (java.net)
MotivationThe current implementation of the String class stores characters in a char array, using two bytes (sixteen bits) for each character. Data gathered from many different applications ...
九、执行引擎
[toc]
9. 执行引擎9.1. 执行引擎概述执行引擎属于 JVM 的下层,里面包括解释器、及时编译器、垃圾回收器
执行引擎是 Java 虚拟机核心的组成部分之一。
“虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是==物理机的执行引擎是直接建立在==处理器、缓存、指令集和==操作系统层面上的==,而虚拟机的执行引擎则是由软件自行实现的,因此可以不受物理条件制约地定制指令集与执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式。
JVM 的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被 JVM 所识别的字节码指令、符号表,以及其他辅助信息。
那么,如果想要让一个 Java 程序运行起来,执行引擎(Execution Engine)的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。简单来说,JVM 中的执行引擎充当了将高级语言翻译为机器语言的译者。
9.1.1. 执行引擎的工作流程
执行引擎在执行的过程中究竟需要执行什么样的字节码指令完 ...
八、对象实例化与直接内存
[toc]
8. 对象实例化及直接内存8.1. 对象实例化面试题
美团:
对象在 JVM 中是怎么存储的?
对象头信息里面有哪些东西?
蚂蚁金服:
Java 对象头有什么?
8.1.1. 创建对象的方式
代码演示
123456789/** * @author shkstart shkstart@126.com * @create 2020 17:16 */public class ObjectTest { public static void main(String[] args) { Object obj = new Object(); }}
结果分析
8.1.2. 创建对象的步骤前面所述是从字节码角度看待对象的创建过程,现在从执行步骤的角度来分析:
1. 判断对象对应的类是否加载、链接、初始化虚拟机遇到一条 new 指令,首先去检查这个指令的参数能否在 Metaspace 的常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化(即判断类元信息是否存在)。
如果没有,那么 ...
七、方法区
[toc]
7. 方法区
从线程共享与否的角度来看
7.1. 栈、堆、方法区的交互关系
7.2. 方法区的理解官方文档:Chapter 2. The Structure of the Java Virtual Machine (oracle.com)
7.2.1. 方法区在哪里?《Java 虚拟机规范》中明确说明:“尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。”但对于 HotSpotJVM 而言,方法区还有一个别名叫做 Non-Heap(非堆),目的就是要和堆分开。
所以,方法区看作是一块独立于 Java 堆的内存空间。
代码层面也可以进行演示:
7.2.2. 方法区的基本理解
方法区(Method Area)与 Java 堆一样,是各个==线程共享==的内存区域。(当多个线程都需要调用某个类时,并且该类没有被加载,只需要其中一个线程加载即可)
方法区在 JVM 启动的时候被创建,并且它的实际的物理内存空间中和 Java 堆区一样都可以是不连续的。
方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
==方法区的大小 ...
六、堆
[toc]
6. 堆6.1. 堆(Heap)的核心概述堆针对一个 JVM 进程来说是唯一的,也就是一个进程只有一个 JVM,但是进程包含多个线程,他们是共享同一堆空间的。
一句话:==一个进程对应一个JVM实例,一个进程包含多个线程.一个进程中的多个线程共享堆空间,方法区.每个线程各自有一套自己的程序计数器,本地方法栈,虚拟机栈.==
一个 JVM 实例只存在一个堆内存,堆也是 Java 内存管理的核心区域。
Java 堆区在 JVM 启动的时候即被创建,其空间大小也就确定了。是 JVM 管理的最大一块内存空间。
==堆内存的大小是可以调节的==。
代码演示:
123456789101112131415161718192021222324252627282930313233343536/** * -Xms10m -Xmx10m:初始堆空间和最大堆空间设置都为10M * @author shkstart shkstart@126.com * @create 2020 16:41 */public class HeapDemo { public stati ...
五、本地方法栈
[toc]
5. 本地方法接口和本地方法栈5.1. 什么是本地方法?简单地讲,一个 Native Method 是一个 Java 调用非 Java 代码的接囗。一个 Native Method 是这样一个 Java 方法:该方法的实现由非 Java 语言实现,比如 C。这个特征并非 Java 所特有,很多其它的编程语言都有这一机制,比如在 C++中,你可以用 extern “c” 告知 c++编译器去调用一个 c 的函数。
A native method is a Java method whose implementation is provided by non-java code.
在定义一个 native method 时,并不提供实现体(有些像定义一个 Java interface),因为其实现体是由非 java 语言在外面实现的。
本地接口的作用是融合不同的编程语言为 Java 所用,它的初衷是融合 C/C++程序。
举例
1234567891011121314151617public class IHaveNatives { //native与abs ...
四、虚拟机栈
[toc]
4. 虚拟机栈4.1. 虚拟机栈概述4.1.1. 虚拟机栈出现的背景由于跨平台性的设计,Java 的指令都是根据栈来设计的。不同平台 CPU 架构不同,所以不能设计为基于寄存器的。
优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。
4.1.2. 初步印象有不少 Java 开发人员一提到 Java 内存结构,就会非常粗粒度地将 JVM 中的内存区理解为仅有 Java 堆(heap)和 Java 栈(stack)?为什么?
4.1.3. 内存中的栈与堆栈是运行时的单位,而堆是存储的单位
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。
堆解决的是数据存储的问题,即数据怎么放,放哪里
4.1.4. 虚拟机栈基本内容Java 虚拟机栈是什么?Java 虚拟机栈(Java Virtual Machine Stack),早期也叫 Java 栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用,是线程私有的。
生命周期生命周期和线程一致
作用主管 Java 程 ...



