JVM 运行时数据区域

简介

要去了解 Java 虚拟机,我们必须要先了解它的组成。那么 Java 虚拟机是如何定义运行时数据区域的呢?在运行时数据区域中在《Java 虚拟机规范(Java SE 7)》规定下主要为:
1. 程序计数器
2. Java 虚拟机栈
3. 本地方法栈
4. Java 堆
5. 方法区
6. 运行时常量池
7. 直接内存

程序计数器

程序计数器(Program Counter Register)是一块很小的内存,计算机科学中,计数器作为指令位置的一个行号指示器,取出当前的指令后,计数器会加一指向下一条指令。Java 的指令,分支,循环,跳转,异常处理,线程恢复等都依赖于程序计数器。多线程时,每个线程都会有各自独立的程序计数器。

当执行 Java 方法时,如果为一个 Native 修饰的方法,则计数器的值为空(Undefined)

Java 虚拟机栈

Java 虚拟机栈(Java Virtual Marchine Stacks)又被粗糙地称为栈内存是线程私有的,它的生命周期与线程相同。JVM 栈描述了 Java 方法执行的内存模型,每一次方法调用都会创建一个栈帧(Stack Frame)用于保存局部变量表、操作数栈、动态链接、方法出口等信息。方法执行与结束的过程对应着JVM 栈的入栈和出栈过程。

局部变量表存放了Java的基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型, 一个对象句柄或者对象指针又或者对象位置)、returnAddress 类型 (一条字节码的地址)。一个局部变量空间大致为32位。局部变量表的空间不会动态分配。

本地方法栈

本地方法栈(Native Method Stack)Java 虚拟机栈通常是一致的,不过为 Native Method 服务,在 Sun HotSpot VM 中,与Java 虚拟机栈合二为一

Java 堆

Java堆(Java Heap)是 JVM 管理的最大的,被所有线程共享的内存区域,用来保存大多数的对象实例,但这不是“绝对”的。Java 的垃圾回收集中在 Java 堆上,因此又被称为 GC 堆(Garbage Collected Heap)。

从内存回收的角度来看,由于现在收集器基本都采用分代回收算法,所以 Java 堆中分为:新生代和老年代。细致地可以分为:Eden 空间、From Survivor 空间、To Survivor 空间。
从内存分配角度来看,Java堆可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。

可以通过 -Xmx -Xms 来控制 Java 堆的大小。

方法区

方法区(Method Area)与 Java 堆一样,是各个线程共享的内存区域,用于储存已经被虚拟机加载的类信息、常量、静态变量、即时翻译器编译后的代码等数据。为了区别方法区和堆通常,方法区有 Non-Heap 的别名。

通常不会对方法区进行内存回收因此又被称为(Java 堆的永久代),但这个区域是会被回收的。对这个区域的 GC 一般是常量池的回收和类型的卸载。

方法区空间的大小会受到 -XX:MaxPermSize 的影响。

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分,Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table)用于存放编译期生成的各种字面量和符号引用,这些存放在类加载后进入方法区的运行时常量池中存放。

直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,但也是一块重要的内存区域,避免了使用 NIO(New Input/Output)时,在Java 堆与Native 堆之间的数据交换。DirectByteBuffer 对象作为这块内存的引用。

了解完 JVM 运行时的内存区域,我们能够更加深入地去了解 JVM 中各区域的运行细节!

Java 历史及特性

总结自:深入理解Java虚拟机:JVM高级特性与最佳实践 第2版

Java 历史

年份 纪事
1991年4月 James Gosling 的 Green Project 开始执行,该“绿色项目”的结果是 Oak(Java 前身)。
1995年5月23日 Oak 改名为 Java 并在 SunWorld 发布 Java 1.0 提出 “Write Once, Run Anywhere(一次编写到处执行) ”的口号
1996年1月 Sun Classic VM 发布
1996年4月 10个主要操作系统供应商嵌入 Java 技术,大约 8.3 万网站使用了 Java, 首次举行 JavaOne 这个后来的 Java 技术大会
1997年2月19日 JDK1.1发布,添加了新技术和Java语法
1997年2月19日 ~ 1999年4月8日 JDK1.1.0 ~ JDK 1.1.8发布: 1.1.4-Sparkler, 1.1.5-Pumpkin, 1.1.6-Abigai, 1.1.7-Brutus, 1.1.8-Chelsea
1998年12月4日 里程碑式版本 JDK 1.2-Playground 发布,分为三个版本:J2MEJ2SEJ2EE
1999年3月 JDK1.2.1
1999年7月 JDK 1.2.2
1999年4月27日 HotPot VM 发布,JDK 1.2 可选 VM, JDK 1.3 后默认的 VM 。
2000年5月8日 JDK 1.3-Kstrel, 对数学运算作出了改进,JNDI 作为平台服务,基于 CORBA IIOP 实现 RMI 通讯协议, Java 2D API,Timer API改进以及添加了 JavaSound 库
2001年5月17日 JDK-1.3.1-Ladybird
2002年2月13日 JDK-1.4-Merlin 发布,这是第一个走向成熟的版本。
2002年9月16日 JDK-1.4.1-Grasshopper
2003年6月26日 JDK-1.4.2-Mantis
2004年9月30日 JDK-1.5-Tiger 语法易用性上的改进,以及改进内存模型,提供了 java.util.concurrent 并发包。最后一个支持 Windows 9.x 平台的 Java。
2006年12月11日 JDK-1.6-Mustang 结束 J2SE、J2EE、J2ME 的命名, 启用 Java SE 6、 Java EE 6、Java ME 6 命名方式。
2006年11月13日 OpenJDK 初现。
2009年2月19日 JDK-1.7-Dolphin 第一个JDK 1.7规划的里程碑版本发布。于 2010年9月9日 所有的JDK 1.7 里程碑才完成。没有按计划完成
2012年10月16日 在 JDK 1.6发布后,Sun 公司,由于代码复杂性、JDK 开源、开发 JavaFX、经济危机、Sun 公司收购案等原因,Sun 公司被拖累。无法再进行两年一主版本。 JDK 1.6 一共发布了 37个更新

Java 特性

JDK 1.0

  1. JVM
  2. Applet (网页小程序)
  3. AWT

JDK 1.1

  1. *.jar 文件格式
  2. JDBC
  3. JavaBeans
  4. RMI
  5. 内部类 (Inner Class)
  6. 反射(Reflection)

JDK 1.2

  1. EJB
  2. Java Plug-in
  3. Java IDL
  4. Swing
  5. JIT
  6. strictfp 关键字
  7. Collections 集合类

JDK 1.3

  1. Timer API
  2. 平台级 JNDI
  3. 基于 CORBA IIOP 实现的 RMI
  4. 改进的 Java 2D
  5. JavaSound

JDK 1.4

  1. 正则表达式
  2. 异常链
  3. NIO
  4. 日志类
  5. XML 解释器
  6. XSLT 转换器

JDK 1.5

  1. 自动装箱
  2. 泛型
  3. 动态注解
  4. 枚举
  5. 可变长参数
  6. 遍历循环(foreach 循环)

JDK 1.6

  1. 启用动态语言支持(Mozilla JavaScript Rhino)
  2. 提供编译API
  3. 微型HTTP服务器 API
  4. 锁与同步改进
  5. 垃圾收集算法改进
  6. 类加载算法改进

JDK 1.7

  1. G1收集器
  2. 非Java语言调用支持(JSR-292)
  3. 升级类加载架构