From 4f28b59057d3f2264f35faf426e8d5a3a7e93d80 Mon Sep 17 00:00:00 2001 From: Guide Date: Sat, 20 Apr 2024 14:55:36 +0800 Subject: [PATCH] =?UTF-8?q?[docs=20update]=E8=A1=A5=E5=85=85jvm=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E5=B9=B4=E9=BE=84=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/java/basis/java-basic-questions-01.md | 2 +- docs/java/jvm/memory-area.md | 30 +++++++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/docs/java/basis/java-basic-questions-01.md b/docs/java/basis/java-basic-questions-01.md index 06134117..9e7a5c5e 100644 --- a/docs/java/basis/java-basic-questions-01.md +++ b/docs/java/basis/java-basic-questions-01.md @@ -18,7 +18,7 @@ head: ### Java 语言有哪些特点? -1. 简单易学; +1. 简单易学(语法简单,上手容易); 2. 面向对象(封装,继承,多态); 3. 平台无关性( Java 虚拟机实现平台无关性); 4. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持); diff --git a/docs/java/jvm/memory-area.md b/docs/java/jvm/memory-area.md index fe47ab6a..c1f9c60c 100644 --- a/docs/java/jvm/memory-area.md +++ b/docs/java/jvm/memory-area.md @@ -125,10 +125,25 @@ Java 堆是垃圾收集器管理的主要区域,因此也被称作 **GC 堆( **JDK 8 版本之后 PermGen(永久代) 已被 Metaspace(元空间) 取代,元空间使用的是本地内存。** (我会在方法区这部分内容详细介绍到)。 -大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 S0 或者 S1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 `-XX:MaxTenuringThreshold` 来设置。但是设置的值应该在0-15,否则会爆出以下错误 ->```MaxTenuringThreshold of 20 is invalid; must be between 0 and 15``` -关于为什么只能是0-15,是因为关于年龄的数据存放在对象头中,对象头只占有4个字节,是0000到1111所以是0-15 -![image](https://github.com/Snailclimb/JavaGuide/assets/167538235/af3ae708-9d66-415d-90f9-fb12c57bb7d3) +大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 S0 或者 S1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 `-XX:MaxTenuringThreshold` 来设置。不过,设置的值应该在 0-15,否则会爆出以下错误: + +```bash +MaxTenuringThreshold of 20 is invalid; must be between 0 and 15 +``` + +**为什么年龄只能是 0-15? ** + +因为记录年龄的区域在对象头中,这个区域的大小通常是 4 位。这 4 位可以表示的最大二进制数字是 1111,即十进制的 15。因此,对象的年龄被限制为 0 到 15。 + +这里我们简单结合对象布局来详细介绍一下。 + +在 HotSpot 虚拟机中,对象在内存中存储的布局可以分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。其中,对象头包括两部分:标记字段(Mark Word)和类型指针(Klass Word)。关于对象内存布局的详细介绍,后文会介绍到,这里就不重复提了。 + +这个年龄信息就是在标记字段中存放的(标记字段还存放了对象自身的其他信息比如哈希码、锁状态信息等等)。`markOop.hpp`定义了标记字(mark word)的结构: + +![标记字段结构](https://oss.javaguide.cn/github/javaguide/java/jvm/hotspot-markOop.hpp..png) + +可以看到对象年龄占用的大小确实是 4 位。 > **🐛 修正(参见:[issue552](https://github.com/Snailclimb/JavaGuide/issues/552))**:“Hotspot 遍历所有对象时,按照年龄从小到大对其所占用的大小进行累加,当累加到某个年龄时,所累加的大小超过了 Survivor 区的一半,则取这个年龄和 `MaxTenuringThreshold` 中更小的一个值,作为新的晋升年龄阈值”。 > @@ -317,9 +332,12 @@ Java 对象的创建过程我建议最好是能默写出来,并且要掌握每 ### 对象的内存布局 -在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:**对象头**、**实例数据**和**对齐填充**。 +在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:**对象头(Header)**、**实例数据(Instance Data)**和**对齐填充(Padding)**。 -**Hotspot 虚拟机的对象头包括两部分信息**,**第一部分用于存储对象自身的运行时数据**(哈希码、GC 分代年龄、锁状态标志等等),**另一部分是类型指针**,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 +对象头包括两部分信息: + +1. 标记字段(Mark Word):用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等。 +2. 类型指针(Klass Word):对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 **实例数据部分是对象真正存储的有效信息**,也是在程序中所定义的各种类型的字段内容。