浅谈 Java 内存区域(二)

70 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 23 天,点击查看活动详情

前言

上一篇文章,我们介绍了 Java 内存区域中线程私有的程序计数器、虚拟机栈、本地方法栈和线程共有的堆,承接上一篇文章:浅谈 Java 内存区域(一) 我们今天继续来看看剩下的内存区域吧!

方法区

方法区与堆一样,是线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

在JDK8以前,方法区被称为“永久代”。

如果方法区无法满足新的内存分配需求时,将抛出 OutOfMemoryError 异常。

运行时常量池

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

一般来说,除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中。

运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量 一定只有编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中,如String类的intern()方法。

当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。

直接内存

直接内存并不是 JVM 运行时数据区的一部分,但这部分内存也被频繁使用,也可能导致 OutOfMemoryError 异常出现。

在 JDK 1.4 中新加入了 NIO 类,引入了一种基于通道 Channel 与缓冲区 Buffer 的I/O方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里面的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java堆 和 Native 堆中来回复制数据。