### 学习深入理解Java虚拟机的前几章笔记
#### JVM内存模型
Java虚拟机(JVM)的内存模型主要分为两大类:线程共享区和线程私有区。
##### 线程共享区
- **堆**:是所有线程共享的内存区域,在这里存放着对象实例。堆内存又被细分为新生代和老年代,这是基于对象生命周期的不同阶段而进行的划分。
- **新生代**:通常分为一个较大的Eden区和两个较小的Survivor区(S0和S1),默认比例为8:1:1。每次垃圾回收时,对象都会从Eden区或其中一个Survivor区移动到另一个Survivor区,经过几次生存周期后,幸存的对象会被提升到老年代。
- **老年代**:存放较长时间存活的对象以及大对象,如长数组等。
- **方法区**:同样被所有线程共享,它主要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。方法区也会出现“内存溢出”的异常,即当方法区无法满足新的内存分配需求时,将抛出`OutOfMemoryError`异常。
##### 线程私有区
- **虚拟机栈**:每个线程拥有一个独立的栈,栈中保存了方法执行的局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从执行到结束的过程都对应着一个栈帧在栈中的入栈和出栈过程。
- **本地方法栈**:与虚拟机栈所发挥的作用相似,区别在于虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。
- **程序计数器**:是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。对于执行native方法的线程而言,程序计数器的值为空(`Undefined`)。
#### 对象的创建过程
对象的创建过程可以概括为以下几个步骤:
1. **加载类**:首先需要确保类已经被加载、解析和初始化。
2. **分配内存**:在堆中为新对象分配内存。
3. **初始化零值**:将分配的内存初始化为零值,这一步是为了保证对象的实例字段在Java代码中可以不赋初始值就直接使用。
4. **设置对象头**:对对象头进行必要的初始化,包括该对象的类信息、哈希码、GC分代年龄等信息。
5. **执行初始化代码**:执行`<init>`构造函数,对对象进行初始化。
#### 垃圾回收算法
垃圾回收算法主要关注如何自动回收不再使用的内存,从而避免内存泄漏。
- **标记-清除算法**:首先标记所有需要回收的对象,然后一次性地清理掉被标记的对象。这种算法存在效率低下和内存碎片化的问题。
- **复制算法**:将内存划分为两块相等的部分,每次只使用其中的一部分。当这部分内存用尽时,将还存活的对象复制到另一块,然后清除已使用过的内存。这种方法的优点是可以避免内存碎片的产生,但代价是使用了更多的内存空间。
- **标记-整理算法**:结合了标记-清除算法和复制算法的优点。它首先标记出需要回收的对象,然后将存活的对象移动到内存的一端,再清理掉末端的空白区域。这种方法既能避免内存碎片,又能提高内存利用率。
#### 内存分配与回收策略
在JVM中,不同的内存区域有着不同的分配与回收策略:
- **年轻代**:主要采用复制算法,因为年轻代中的对象大部分会在短时间内被销毁。
- **老年代**:主要采用标记-整理算法,因为老年代的对象一般具有较长的生命周期,而且对象之间的相互引用比较复杂,不适合频繁的复制操作。
- **方法区**:由于方法区主要存放静态数据和常量,这些数据一般不会频繁变更,因此垃圾回收的压力相对较小。
了解这些基础概念有助于更好地掌握Java虚拟机的工作原理,并有助于优化应用程序的性能。