方法区
方法区主要存放的是已被虚拟机加载的类信息、常量、静态变量、编译器编译后的代码等数据。
由于永久代存储的数据生命周期非常长,GC在这个区域消耗时间长且回收效果差,所以方法区GC频率很低。
不同回收策略对方法区的处理:
G1:只有在Full GC的时候方法区才会被清理。当方法区需要空间时,该空间无法扩展又没有内存空间可以回收,就会抛出OutOfMemory异常。
CMS:可以使用 -XX:+CMSClassUnloadingEnabled 参数,在CMS并行收集阶段回收 PermGen 空间。
运行时常量池
运行时常量池也是方法区的一部分,用于存放编译器生成的各种字面量和符号引用。
运行时常量池除了编译期产生的Class文件的常量池,还可以在运行期间,将新的常量加入常量池,比较常见的是String类的intern()方法。
字面量:与Java语言层面的常量概念相近,包含文本字符串、声明为final的常量值等。
符号引用:编译语言层面的概念,包括以下3类:
类和接口的全限定名
字段的名称和描述符
方法的名称和描述符
但是该区域不会抛出 OutOfMemoryError 异常。
直接内存
直接内存不是虚拟机运行时内存的一部分,该空间划分在虚拟机外。
不过由于直接内存的性能比较好,所以有的工作需要使用直接内存来提高性能。
直接内存会受到物理机剩余可用内存、处理器寻址空间的限制。
可以通过NIO和NIO.2来申请直接内存。如果虚拟机堆内存分配太大,可能会导致直接内存空间不足而出现运行时异常。
变化
元数据区 Metaspace
由于 PermGen 内存管理的效果远没有达到预期,所以JCP已经着手去除PermGen的工作。在JDK7中,字符串常量已经从永久代移除。
现今 JDK8 中 PermGen 已经被彻底移除,取而代之的是metaspace数据区,使用native内存,申请和释放由虚拟机负责管理。
在JDK8下,旧的参数 -XXermSize 和 -XX:MaxPermSize 会被忽略并显示警告。
新的Metaspace通过参数 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 设定。
G1 内存模型
从JDK7开始引入的G1回收机制,到JDK8时G1已经基本稳定。
G1应该是唯一一个能完成新生代到老年代所有管理的GC技术。
之前的技术如上一节图中CMS和PerNew需要互相配合才能完成完整回收工作。
基于G1回收的特殊性,G1的内存模型和上面介绍的内存模型是有差别的。也就是说,上面的内存模型对G1是无效的。
由于现在还没有太多的资料可以参考G1的内存模型。
如果有需要深入了解,请阅读OpenJDK中HotSpot JVM关于G1的源码。
如果没有特殊要求,现时CMS和PerNew已经很好适应大部分应用场景。毕竟CMS用了多年,出现问题也容易找到解决方法。 |