对象内存布局
对象的内存布局是由 Java 虚拟机规范定义的,但具体的实现细节各有不同,如 HotSpot 和 OpenJ9 就不一样
常用的 HotSpot 为例:
对象在内存中包括三部分:对象头、实例数据和对齐填充
对象头 (Mark World + 类型指针 (指向 Class 对象)) + 实例数据 + 对齐填充
对象头 (8字节 + 8字节 (压缩 4 字节)): 12字节 (指针压缩)
对齐填充必须是 8 的倍数 (64位)
好好学习,天天向上
对象的内存布局是由 Java 虚拟机规范定义的,但具体的实现细节各有不同,如 HotSpot 和 OpenJ9 就不一样
常用的 HotSpot 为例:
对象在内存中包括三部分:对象头、实例数据和对齐填充
对象头 (Mark World + 类型指针 (指向 Class 对象)) + 实例数据 + 对齐填充
对象头 (8字节 + 8字节 (压缩 4 字节)): 12字节 (指针压缩)
对齐填充必须是 8 的倍数 (64位)
Java 堆被划分为新生代和老年代两个区域
新生代又被划分为 Eden 空间和两个 Survivor 空间(From 和 To)
新创建的对象会被分配到 Eden 空间
当 Eden 区填满时,会触发一次 Minor GC,清除不再使用的对象
存活下来的对象会从 Eden 区移动到 Survivor 区。
对象在新生代中经历多次 GC 后,如果仍然存活,会被移动到老年代
当老年代内存不足时,会触发 Major GC,对整个堆进行垃圾回收
内存溢出,俗称 OOM,是指当程序请求分配内存时,由于没有足够的内存空间,从而抛出 OutOfMemoryError
List<String> list = new ArrayList<>();
while (true) {
list.add("OutOfMemory".repeat(1000)); // 无限增加内存
}
垃圾回收就是对堆内存中不再被引用的(不可达的)对象进行清除或回收
JVM 在做 GC 之前,会先搞清楚什么是垃圾,什么不是垃圾,通常会通过可达性分析算法来判断对象是否存活
在确定了哪些垃圾可以被回收后
垃圾收集器(如 CMS、G1、ZGC)要做的事情就是进行垃圾回收
可以采用:
C++ 等语言创建对象要不断的去开辟空间,不用的时候又需要不断的去释放空间,既要写构造函数,又要写析构函数
于是,有人就提出,能不能写一段程序实现这块功能,每次创建对象、释放内存空间的时候复用这段代码
1960 年,John McCarthy 在设计 Lisp 语言时首次提出了垃圾回收的概念,用于自动管理内存,避免程序员手动释放内存带来的错误
"Stop The World"是 Java 垃圾收集中的一个重要概念。在垃圾收集过程中,JVM 会暂停所有的用户线程,这种暂停被称为"Stop The World"事件
这么做的主要原因是为了防止在垃圾收集过程中,用户线程修改了堆中的对象,导致垃圾收集器无法准确地收集垃圾
值得注意的是,"Stop The World"事件会对 Java 应用的性能产生影响
如果停顿时间过长,就会导致应用的响应时间变长,对于对实时性要求较高的应用,如交易系统、游戏服务器等,这种情况是不能接受的。
因此,在选择和调优垃圾收集器时,需要考虑其停顿时间。Java 中的一些垃圾收集器,如 G1 和 ZGC,都会尽可能地减少了"Stop The World"的时间,通过并发的垃圾收集,提高应用的响应性能。
学习 JVM 可以帮助我们开发者更好地优化程序性能、避免内存问题
所以主要内容:内存模型、垃圾回收机制、类加载机制、JVM 调试/监控 工具
JVM 启动后,大致可以分为三个功能区:类加载器、运行时数据区以及执行引擎
内存区其实是功能区中"运行时数据区"的具体细分
JVM(功能区视角)
│
├── 类加载器
│
├── 运行时数据区 ←──── 这部分就是"内存区"
│ ├── 程序计数器
│ ├── 虚拟机栈
│ ├── 本地方法栈
│ ├── 堆
│ └── 方法区
│
└── 执行引擎
├── JIT 编译器
└── 垃圾回收器
当 Java 应用需要与操作系统底层或硬件交互时,通常会用到本地方法栈。
native 方法是在 Java 中通过 native 关键字声明的,用于调用非 Java 语言,如 C/C++ 编写的代码。Java 可以通过 JNI,也就是 Java Native Interface 与底层系统、硬件设备、或者本地库进行交互
Java 的 native 方法本身没有 Java 代码实现,而是交给本地代码实现
public native void test();
虚拟机栈、本地方法栈、程序计数器 | 堆、方法区
主要是方法区实现的不同
方法区是 JVM 规范里的概念, JVM 需要有这么一块区域来存类相关信息
HotSpot 实现层面:曾经用永久代、后来用元空间来实现
JDK 1.6 使用永久代来实现方法区:
JDK 1.7 时仍然是永久带,但发生了一些细微变化,比如将字符串常量池、静态变量存放到了堆上。
Java 没有 Ready 状态,线程状态有 6 种:
NEW 新建
RUNNABLE 可运行(包含 Ready + Running)
BLOCKED 阻塞(等待锁)
WAITING 等待(wait/join/park 等)
TIMED_WAITING 限时等待
TERMINATED 终止