深入理解Java虚拟机(一):自动内存管理机制

2018-12-30  本文已影响3人  wch853

Java虚拟机

Oracle有两款实现了 Java SE 的产品:Java SE Development Kit(JDK)Java SE Runtime Environment(JRE)
JDKJRE 的超集,包含了 JRE 的所有内容,以及开发应用程序所需的编译器和调试器等工具。 JRE 提供了函数库、Java Virtual Machine(JVM) 和其它用来运行Java应用程序的组件。

Java SE8产品组件概念图

Java内存区域

Java虚拟机运行时数据区

程序计数器

程序计数器是一块较小的内存空间,他的作用可以看做是当前线程所执行的字节码的行号指示器。由于 JVM 的多线程是通过线程轮流切换并分配处理器执行时间来实现的,因此每个线程都有一个独立的程序计数器,用于线程切换后能恢复到正确的执行位置。如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是 Native方法,这个计数器的值为 undefined。此区域是唯一一个在Java虚拟机规范中没有规定 OutOfMemoryError 情况的区域。

Java虚拟机栈

本地方法栈

Java虚拟机栈为虚拟机执行Java方法服务,本地方法栈则为虚拟机使用到的 Native方法 服务。在 Hotspot虚拟机 的实现中是把本地方法栈和虚拟机栈合二为一的。与虚拟机栈一样,本地方法栈也会抛出 StackOverFlowError 异常和 OutOfMemoryError 异常。

Java堆

堆是线程共享的数据运行时区域,几乎所有的对象实例以及数组都要在堆上分配内存。堆是垃圾收集器管理的主要区域。Java堆可以处于物理上不连续的内存空间中。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出 OutOfMemoryError 异常。

方法区

方法区存储虚拟机加载的类信息(类的版本、字段、方法、接口)、常量、静态变量、即时编译器编译后的代码等数据。于 Hotspot虚拟机 来说,将方法区纳入GC管理范围,这样就不必单独管理方法区的内存,所以就有了相对于新生代和老年代的永久代一说。

运行时常量池

运行时常量池(JDK6在方法区,JDK7在Java堆)用来存放编译器生成的各种字面量以及符号引用(类加载之后进入运行时常量池)。运行期间也能将新的常量放入池中。当常量池无法再申请到内存时,将会抛出 OutOfMemoryError 异常。

直接内存

Java NIO 使用 Native函数库 直接分配堆外内存,然后通过一个存储在Java堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。通过避免在 Java堆Native堆 中来回复制数据来提高性能。直接内存大小不受虚拟机参数控制,如果各个内存区域总和大于物理内存限制,就会出现 OutOfMemoryError 异常。

对象

对象的创建

对象的创建过程

内存分配策略

根据 Java 堆 是否规整可以判断使用哪种内存分配策略。

线程安全性

对象创建在虚拟机中是十分频繁的行为,在并发环境下需要考虑线程安全。

对象设置

将对象的哈希吗、GC年龄信息等存放在对象头中,执行 <init> 方法。

对象的结构

对象头(Header)

实例数据(Instance Data)

实例数据是对象真正存储的有效信息,也是程序代码中所定义的各种类型的字段内容。

对齐填充(Padding)

HotSpot虚拟机 要求对象的起始地址必须是8字节的整数倍,也就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐的时候,就需要通过对齐填充来补全。

对象的访问定位

Java程序通过栈上的 reference 数据来操作堆上的具体对象,由于 reference 类型在Java虚拟机规范中只规定了一个指向对象的引用,具体用何种方式去定位、引用堆中对象的具体位置,取决于虚拟机的实现,目前主要有 使用句柄直接指针 两种方式。

垃圾回收

在Java堆上分配一个内存给实例对象时,此时在虚拟机栈上引用型变量就会存放这个实例对象的起始地址。当线程销毁后,其在虚拟机栈上的内存自然会被回收,也就是说虚拟机栈上的这块内存不在虚拟机GC范围内。

垃圾对象判定算法

Java引用

finalize()

将对象回收至少要经历两次标记过程,如果在可达性分析中发现对象没有与 GC Roots 的引用链,那它将会被第一次标记并被进行一次筛选,筛选的条件是此对象是否有必要执行 finalize() 方法(当前对象没有覆盖此方法或者已经执行过此方法,则虚拟机认为“没有必要执行”),虚拟机不会承诺等待此方法执行结束。如果在 finalize() 方法中成功与引用链上的人一个对象建立关联,则对象不会被回收。

如何回收

回收策略

Java垃圾回收管理

垃圾收集器

垃圾收集器是垃圾回收算法的具体实现。

Serial垃圾收集器

Serial垃圾收集器 是最基本、发展历史最悠久的收集器。

Serial - Serial Old组合收集器

ParNew垃圾收集器

ParNew垃圾收集器 是Serial收集器的多线程版本。

ParNew -Serial Old组合收集器

Parallel Scavenge垃圾收集器

Parallel Scavenge收集器 的目标是达到一个可控制的吞吐量(CPU用于运行用户代码的时间与CPU消耗的总时间的比值),即减少垃圾收集时间,让用户代码获得更长的运行时间。

CMS(Concurrent Mark Sweep)垃圾收集器

CMS收集器是一款真正意义上的并发收集器,实现了让垃圾收集线程与用户线程(基本上)同时工作。适用于与用户交互较多的场景。

G1垃圾收集器

内存分配

HotSpot一般的年代内存划分

对象的内存分配主要在堆上分配(JIT编译优化后可能在栈上分配),在新生代的Eden空间中分配,如果启用了本地线程分配缓冲,则线程优先在TLAB上分配。少数情况下对象会直接分配在老年代中。

内存分配策略

上一篇下一篇

猜你喜欢

热点阅读