针对JDK6、JDK7、JDK8三个版本的JVM内存模型调整说明

266 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

image.png

对永久代PermGen的说明

  1. 永久代是方法区在hotspot的一个具体实现。通过在运行时数据区域卉辟空间实现方法区。
  2. hotspot jdk7之前的永久代,比较完整
  3. 从jdk7以后方法区就"四分五裂了”,不再是在单一的一个去区域内进行存储
  4. 在jdk8,移除了永久代,被Metsspace取代了,且Metsspace不在JVM堆内,放入了本地内存,元空间也就成了方法区的主要存放位置

绝大部分Java程序员应该都见过java.lang.OutOfMemoryError: PermGen space这个异常 这里的“PermGen space"其实指的就是方法区。不过方法区和"PermGen space"又有着本质的区别。前者是JVM的规范,而后者则是JVM规范的一种实现,并且只有HotSpot 才有“PermGen space”,而对于其他类型的虚拟机,如JRockit (Oracle) . J9 (IBM)并没有"PermGen space"”。

由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。最典型的场景就是,在jsp页面比较多的情况,容易出现永久代内存溢出(每请求一次jsp页面,会产生一个session对象,并且这个对象30分钟后才过期。我们计算了下当时的QPS是5000,也就是说每秒钟产生5000个session对象。每分钟产生300K个对象,session是个map对象,比较大,这样很快就会把内存撑爆。)

对Metaspace元空间的说明

1.元空间的本质和永久代类似,都是对VM规范中方法区的实现。元空间通过在本地内存区域开辟空间实现方法区。元空间并不在虚拟机中,而是使用本地内存,所以默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

image.png

其实,移除永久代的工作从JDK1.7就开始了,JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是Native Heap。但永久代仍存在于JDK1.7中,并没完全移除。 譬如:

  1. 符号引用(Symbols)转移到了native heap
  2. 字面量(interned strings)转移到了java heap
  3. 类的静态变量(class statics)转移到了java heap

元空间特点

  1. 类及相关的元数据的生命周期与类加载器的一致,如果GC发现某个类加载器不再存活了,才会把相关的空间整个回收掉
  2. 每个类加载器有专门的存储空间
  3. 只进行线性分配,省掉了GC扫描及压缩的时间
  4. 元空间里的对象的位置是固定的

JVM内存划分调整的几个原因点分析

  1. 字符串存在永久代中,容易出现性能问题和内存溢出
  2. 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出永久代会为GC带来不必要的复杂度,并且回收效率偏低