JVM 内存区域详解(3)
运行时常量池
Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有用于存放编译期生成的各种字面(Literal)和符号引用(Symbolic Reference)的 常量池表(Constant Pool Table)
字面量是源代码中的固定值的表示法,即通过字面我们就能知道其值的含义。字面量包括整数、浮点数和字符串字面两。常见的符号引用包括类符号引用、字段符号引用、方法符号引用、接口方法符号。
《深入理解 Java 虚拟机》7.34 节第三版对符号引用和直接引用的解释如下:
常量池表会在类加载后存放到方法区的运行时常量池中
运行时常量池的功能类似于传统编程语言的符号表,尽管它包含了比典型符号表更广泛的数据。
既然运行时常量池是方法区的一部分,自然收到方法区的内存限制,当常量池无法再申请到内存时会抛出 OOM 错误
字符串常量池
字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
HotSpot 虚拟机中字符串常量池的实现是 stringTable.cpp
StringTable
可以简单理解为一个固定大小的 HashTable
容量可以通过参数来设定,保存的是字符串 key 和字符串对象的引用的映射关系,字符串对象的引用指向堆中的字符串对象。
JDK 1.7 之前,字符串常量池存放在永久代。JDK 1.7 字符串常量池和静态变量从永久代移动到 Java 堆中。
JDK 1.7 为什么要把字符串常量池移动到堆中
主要是因为永久代 (方法区的实现)的 GC 回收效率太低,对永久代的 GC 需要满足一系列复杂的条件且不一定能够完美回收,而 Java 中对于字符串的创建是非常频繁的,将字符串常量池放入堆中,能够更高效的及时回收字符串内存。
直接内存
直接内存是一种特殊的内存缓冲区域,并不在 Java 堆或方法区中分配的,而是通过 JNI 的方式在本地内存上分配的。
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁的使用。而且也可能导致 OOM 错误出现。
JDK 1.4 中新加入的 NIO (New I/O 也被称作 Non-Blocking I/O),引入了一种基于通道的(Channel)与缓存区 (Buffer)的I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提升性能,因为避免了 Java 堆 Native 堆之间来回复制数据。
直接内存的分配不会收到 Java 堆的限制,但是,既然是内存就会收到本机的总内存大小以及处理器寻址空间的限制。
va 堆的限制,但是,既然是内存就会收到本机的总内存大小以及处理器寻址空间的限制。
类似的概念还有 堆外内存 。 堆外内存就使把内存对象分配在堆之外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的后果就使能够一定程度上减少垃圾回收对应用造成的影响。