Java 虚拟机详解
- 1. Java 虚拟机
-
- 1.1 什么是虚拟机
- 1.2 JVM 由哪些部分组成
- 2. JVM 内存结构
-
- 2.1 程序计数器(线程私有)
- 2.2 虚拟机栈(线程私有)
- 2.3 本地方法区(线程私有)
- 2.4 堆(Heap – 线程共享)- 运行时数据区
-
- 2.4.1 新生代
-
- 2.4.1.1 Eden 区
- 2.4.1.2 ServivorFrom
- 2.4.1.3 ServivorTo
- 2.4.1.4 为什么需要 Survivor
- 2.4.1.5为什么需要 From 和 To 两个呢
- 2.4.2 老年代
- 2.4.3 TLAB
-
- 2.4.3.1 什么是 TLAB (Thread Local Allocation Buffer)
- 2.4.3.2 为什么要有 TLAB
- 2.4.4 堆是分配对象存储的唯一选择吗
-
- 2.4.4.1 逃逸分析
-
- 2.4.4.1.1 代码优化之同步省略(消除)
- 2.4.4.1.2 代码优化之标量替换
- 2.4.4.1.3 代码优化之栈上分配
- 2.4.4.1.4 总结
- 2.5 元数据(线程共享)
-
- 2.5.1 静态常量池
- 2.5.2 运行时常量池
- 2.5.3 字符串常量池
- 2.5.4 总结
- 2.6 直接内存
- 2.7 OutOfMemoryError(OOM) 内存溢出
-
- 2.7.1 Java 堆溢出
- 2.7.1 虚拟机栈和本地方法栈溢出
- 2.7.2 运行时常量池溢出
- 2.7.3 方法区的内存溢出
- 2.7.4 元数据区的内存溢出
- 2.7.5 本机直接内存溢出
- 2.7.6 解决内存溢出
- 3. 垃圾回收算法
-
- 3.1 垃圾回收策略
- 3.2 垃圾回收发生区域
- 3.3 如何确定垃圾
-
- 3.3.1 引用计数法
- 3.3.2 可达性分析
- 3.3.3 不得不说的四种引用
- 3.3.4 生存还是死亡
- 3.4. 垃圾回收算法
-
- 3.4.1 标记-清除算法(Mark-Sweep)
- 3.4.2 复制算法(Copying)
- 3.4.3 标记-整理算法(Mark-Combact)
- 3.4.4 分代收集算法
-
- 3.4.4.1 新生代与复制算法
-
- 3.4.4.1.1 新生代GC(MinorGC/YoungGC)
- 3.4.4.1.2 什么情况下会出现 Young GC
- 3.4.4.2 老年代与标记整理算法
-
- 3.4.4.2.1 老年代GC(MajorGC/FullGC)
- 3.4.4.2.2 什么情况下回出现 Full GC
- 3.4.4.3 对象分配规则是什么
- 3.4.5 分代收集算法的好处
- 3.4.6 分代收集算法调优原则
- 3.4.5 分区(增量)收集算法
- 3.4.6 JVM 相关参数
- 4. GC 垃圾收集器
-
- 4.1 术语
-
- 4.1.1 Stop The Word
- 4.1.2 并行收集 VS 并发收集
- 4.1.3 吞吐量
- 4.1.4 安全点
- 4.2 Serial 垃圾收集器(单线程、复制算法)
- 4.3 ParNew 垃圾收集器(Serial+多线程)
- 4.4 Parallel Scavenge 收集器(多线程复制算法、高效)
- 4.5 Serial Old 收集器(单线程标记整理算法 )
- 4.6 Parallel Old 收集器(多线程标记整理算法)
- 4.7 CMS 收集器(多线程标记清除算法)
-
- 4.7.1 收集过程
-
- 4.7.1.1 初始标记
- 4.7.1.2 并发标记
- 4.7.1.3 重新标记
- 4.7.1.4 并发清除
- 4.7.1.5 线程重置
- 4.7.2 CMS 优点
- 4.7.3 CMS 缺点
- 4.7.4 CMS 使用场景
- 4.8 G1 收集器
-
- 4.8.1 G1 内存模型
-
- 4.8.1.1 G1 堆内存结构
- 4.8.1.2 G1 堆内存分配
- 4.8.2 G1 的 GC 模式
-
- 4.8.2.1 Young GC 年轻代收集
- 4.8.2.1 Mixed GC
-
- 4.8.2.1.1 Mixed GC 执行过程
-
- 4.8.2.1.1.1 初始标记
- 4.8.2.1.1.2 并发标记
- 4.8.2.1.1.3 最终标记
- 4.8.2.1.1.4 筛选回收
- 4.8.2.2 Full GC
-
- 4.8.2.2.1 Full GC 优化原则
- 4.8.3 G1 的优点
- 4.8.4 G1 的使用场景
- 4.9 垃圾收集器总结
- 4.10 垃圾收集器相关参数
1. Java 虚拟机
1.1 什么是虚拟机
??Java 虚拟机,是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件( .class )。
??Java 被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java 虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
??但是,跨平台的是 Java 程序(包括字节码文件),,而不是 JVM。JVM 是用 C/C++ 开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的 JVM 。
??也就是说,JVM 能够跨计算机体系结构来执行 Java 字节码,主要是由于 JVM 屏蔽了与各个计算机平台相关的软件或者硬件之间的差异,使得与平台相关的耦合统一由 JVM 提供者来实现。
??
1.2 JVM 由哪些部分组成
??JVM 运行内存的分类如上图所示,JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【JAVA 堆、方法区】、直接内存。
??线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束 而 创建/销毁(在 Hotspot VM 内, 每个线程都与操作系统的本地线程直接映射, 因此这部分内存区域的存/否跟随本地线程的 生/死对应)。线程共享区域随虚拟机的启动/关闭而创建/销毁。
??直接内存并不是 JVM 运行时数据区的一部分, 但也会被频繁的使用: 在 JDK 1.4 引入的 NIO 提 供了基于 Channel 与 Buffer 的 IO 方式, 它可以使用 Native 函数库直接分配堆外内存, 然后使用 DirectByteBuffer 对象作为这块内存的引用进行操作(详见: Java I/O 扩展), 这样就避免了在 Java 堆和 Native 堆中来回复制数据, 因此在一些场景中可以显著提高性能。
??
2.1 程序计数器(线程私有)
??程序计数器,Java 线程私有,类似于操作系统里的 PC 计数器,它可以看做是当前线程所执行的字节码的行 指示器:
- 如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器值则为空(Undefined);
- 此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区
??
2.2 虚拟机栈(线程私有)
??堆即运行时数据区,所有线程共享的一块区域,JVM 垃圾收集器管理的主要区域:
- 目前主要的垃圾回收算法都是分代收集算法,所以 Java 堆中还可以细分为:新生代和老年代;再细致一点的有 Eden 空间、From Survivor 空间、To Survivor 空间等,默认情况下新生代按照 8:1:1 的比例来分配;
- 根据 Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘一样。
??Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代。
??
2.4.1 新生代
??是用来存放新生的对象。一般占据堆的 1/3 空间。由于频繁创建对象,所以新生代会频繁触发 MinorGC 进行垃圾回收。新生代又分为 Eden 区、ServivorFrom、ServivorTo 三个区。
??
??在 Java8 之前,该块区域为方法区,也叫永久区,指内存的永久保存区域,主要存放 Class 和 Meta(元数据)的信息,Class 在被加载的时候被放入永久区域,它和存放实例的区域不同,GC 不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常。
??在 Java8 中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。元空间的本质和永久代类似,元空间与永久代之间最大的区别在于:
- 元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制;
- 类的元数据放入 native memory, 字符串池和类的静态变量放入 java 堆中, 这样可以加载多少类的元数据就不再由 MaxPermSize 控制, 而由系统的实际可用空间来控制。
??
2.5.1 静态
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!