Java 虚拟机我们简称为 JVM(Java Virtual Machine)。
Java 虚拟机在执行 Java 程序的过程中,会管理几个不同的数据区域。如下图所示:
下面我会介绍这几个数据区的特点。
堆
堆区的几个特点:
- 线程共享。
- 启动时创建堆这个区。
- 基本上所有的对象实例都在这个区分配。
- 物理上不连接(大对象除外)。逻辑上不连接。
- 内存分为新生代和老年代。新生代分为 eden 区和两个大小一样的 survivor 区。
内存细分:
Java 7 及之前内存逻辑上分为三部分:新生区 + 老年代 + 永久代。
- 新生区,又被划分为 Eden 区和 Survivor 区。
- 老年代。
- 永久代实现了方法区。
Java 8 及之后内存逻辑上分为三部分:新生区 + 老年代 + 元空间。
- 新生区,又被划分为 Eden 区和 Survivor 区。
- 老年代。
- 废弃了永久代,使用元空间,它属于本地内存。
方法区
- 线程共享。
- 主要存储这几类信息。
- 类型信息。
- 常量。
- 静态变量。
- 即时编译器编译后的代码缓存。
虚拟机栈
- 线程私有。
- 生命周期与线程相同。
- 一个线程中,每一个方法被执行的时候,创建一个栈帧。
- 栈帧 Stack Frame 的结构。
- 存储局部变量表。
- 基本数据类型。
- 对象引用。
- 返回地址(returnAddress)。
- 操作数栈。
- 动态连接。
- 方法出口。
- 存储局部变量表。
本地方法栈
-
线程私有。
-
虚拟机使用到的本地(Native)方法服务。
程序计数器
-
线程私有。
-
当前线程所执行的字节码的行号指示器。
几个数据区的特点思维导图
垃圾回收
垃圾回收主要关注方法区和堆中的垃圾收集。如下图所示,方法区和堆被高亮显示,用来说明垃圾收集器关心的收集区域。
收集堆区域是垃圾收集器的工作重点。上面我们也讲到了堆空间的划分,包含新生代和老年代,而垃圾收集器会频繁收集新生代,较少收集老年代。
什么是垃圾
我们可以先想下现实生活中的垃圾,比如吃香蕉后的香蕉皮,我们不需要就扔到垃圾桶了,那么香蕉皮就属于垃圾,需要被环卫工人回收。 那 Java 虚拟机中,什么是垃圾呢?
垃圾是指在运行程序中没有任何指针指向的对象,这些对象被当作垃圾被垃圾收集器回收。
如何确定垃圾
有两种算法来确定哪些对象是垃圾:引用计数法和根节点可达性分析。
- 引用计数法
原理:给对象添加一个引用计数器,每当有一个地方引用它,计数器的值就加一。每当有一个引用失效,计数器的值就减一。当计数器值为零时,这个对象被认为没有其他对象引用,可当作垃圾回收。
缺点:需要维护引用计数器,有一定的消耗。且较难处理循环引用的问题。(现在基本没有地方使用这种算法了,了解即可)。
- 可达性分析算法
原理:通过一系称为 GC Roots
的对象作为起始点,从 GC Roots
的对象出发,向下搜索,如果找到的对象和 GC Roots
有直接引用或间接引用关系,则说明这个对象不是垃圾,否则,这个对象就是垃圾。
哪些对象可以当作 GC Roots
- 虚拟机栈中的引用对象。
- 方法区中的类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中的
JNI
(Native 方法)引用的对象。
总结:除了堆空间外的一些结构,比如虚拟机栈、本地方法栈、方法区、字符串常量池等地方对堆空间进行引用的,都可以作为 GC Roots
进行可达性分析。
GC Roots 对象回收
编写程序
package com.test;
public class TestGCRoots {
private static final int _1MB = 1024 * 1024;
private byte[] bigSize = new byte[2 * _1MB];
private static TestGCRoots testGCRoots;
public static void main(String[] args) throws InterruptedException {
testGCRoots = new TestGCRoots();
//gcRootsDemo = null;
System.gc();
}
}
运行程序
$ java -XX:+PrintGCDetails com.test.TestGCRoots
[GC (System.gc()) [PSYoungGen: 5980K->2904K(114176K)] 5980K->2912K(375296K), 0.0024028 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 2904K->0K(114176K)] [ParOldGen: 8K->2689K(261120K)] 2912K->2689K(375296K), [Metaspace: 2672K->2672K(1056768K)], 0.0049802 secs] [Times: user=0.13 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 114176K, used 983K [0x0000000740b80000, 0x0000000748a80000, 0x00000007c0000000)
eden space 98304K, 1% used [0x0000000740b80000,0x0000000740c75da0,0x0000000746b80000)
from space 15872K, 0% used [0x0000000746b80000,0x0000000746b80000,0x0000000747b00000)
to space 15872K, 0% used [0x0000000747b00000,0x0000000747b00000,0x0000000748a80000)
ParOldGen total 261120K, used 2689K [0x0000000642200000, 0x0000000652100000, 0x0000000740b80000)
object space 261120K, 1% used [0x0000000642200000,0x00000006424a07d8,0x0000000652100000)
Metaspace used 2679K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 289K, capacity 386K, committed 512K, reserved 1048576K
断开实例引用
package com.test;
public class TestGCRoots {
private static final int _1MB = 1024 * 1024;
private byte[] bigSize = new byte[2 * _1MB];
private static TestGCRoots testGCRoots;
public static void main(String[] args) throws InterruptedException {
testGCRoots = new TestGCRoots();
gcRootsDemo = null;
System.gc();
}
}
运行程序
$ java -XX:+PrintGCDetails com.test.TestGCRoots
[GC (System.gc()) [PSYoungGen: 5980K->872K(114176K)] 5980K->880K(375296K), 0.0012472 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 872K->0K(114176K)] [ParOldGen: 8K->641K(261120K)] 880K->641K(375296K), [Metaspace: 2674K->2674K(1056768K)], 0.0063510 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 114176K, used 983K [0x0000000740b80000, 0x0000000748a80000, 0x00000007c0000000)
eden space 98304K, 1% used [0x0000000740b80000,0x0000000740c75da0,0x0000000746b80000)
from space 15872K, 0% used [0x0000000746b80000,0x0000000746b80000,0x0000000747b00000)
to space 15872K, 0% used [0x0000000747b00000,0x0000000747b00000,0x0000000748a80000)
ParOldGen total 261120K, used 641K [0x0000000642200000, 0x0000000652100000, 0x0000000740b80000)
object space 261120K, 0% used [0x0000000642200000,0x00000006422a07b8,0x0000000652100000)
Metaspace used 2681K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 289K, capacity 386K, committed 512K, reserved 1048576K
对象和 GC Roots 没有引用关系时(这里引用关系可以是间接或直接引用),即对象不可达,将会被垃圾收集器标记为垃圾,后期被回收掉。